Browse Source

媒体文件处理1.文件上传创建文件夹 2.文件目录查询接口 3.通过目录ID查询文件接口 4.坐标转换 5.exif解析

S0025136190 1 year ago
parent
commit
ccc1fb25a8
28 changed files with 1097 additions and 15 deletions
  1. 6 0
      Backend/sample/pom.xml
  2. 161 0
      Backend/sample/src/main/java/com/dji/sample/common/util/CoordinateUtil.java
  3. 61 0
      Backend/sample/src/main/java/com/dji/sample/common/util/PicExifUtil.java
  4. 0 2
      Backend/sample/src/main/java/com/dji/sample/common/util/UserRequest.java
  5. 2 0
      Backend/sample/src/main/java/com/dji/sample/component/oss/service/IOssService.java
  6. 5 0
      Backend/sample/src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java
  7. 5 0
      Backend/sample/src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java
  8. 16 0
      Backend/sample/src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java
  9. 4 0
      Backend/sample/src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java
  10. 1 1
      Backend/sample/src/main/java/com/dji/sample/manage/controller/DeviceOprLogsController.java
  11. 32 0
      Backend/sample/src/main/java/com/dji/sample/manage/model/common/AMap.java
  12. 25 0
      Backend/sample/src/main/java/com/dji/sample/manage/model/common/AMapProperties.java
  13. 3 0
      Backend/sample/src/main/java/com/dji/sample/manage/model/dto/DeviceDTO.java
  14. 105 0
      Backend/sample/src/main/java/com/dji/sample/manage/model/enums/DeviceStatusEnum.java
  15. 71 0
      Backend/sample/src/main/java/com/dji/sample/media/controller/FileController.java
  16. 12 0
      Backend/sample/src/main/java/com/dji/sample/media/dao/IMediaDirMapper.java
  17. 44 0
      Backend/sample/src/main/java/com/dji/sample/media/model/MediaDirDTO.java
  18. 53 0
      Backend/sample/src/main/java/com/dji/sample/media/model/MediaDirEntity.java
  19. 29 0
      Backend/sample/src/main/java/com/dji/sample/media/model/MediaExifDTO.java
  20. 33 0
      Backend/sample/src/main/java/com/dji/sample/media/model/MediaFileDTO.java
  21. 47 0
      Backend/sample/src/main/java/com/dji/sample/media/model/MediaFileEntity.java
  22. 14 0
      Backend/sample/src/main/java/com/dji/sample/media/service/IFileService.java
  23. 47 0
      Backend/sample/src/main/java/com/dji/sample/media/service/IMediaDirService.java
  24. 157 12
      Backend/sample/src/main/java/com/dji/sample/media/service/impl/FileServiceImpl.java
  25. 147 0
      Backend/sample/src/main/java/com/dji/sample/media/service/impl/MediaDirServiceImpl.java
  26. 2 0
      Backend/sample/src/main/java/com/dji/sample/wayline/service/IWaylineFileService.java
  27. 10 0
      Backend/sample/src/main/java/com/dji/sample/wayline/service/impl/WaylineFileServiceImpl.java
  28. 5 0
      Backend/sample/src/main/resources/application.yml

+ 6 - 0
Backend/sample/pom.xml

@@ -144,6 +144,12 @@
             <version>1.69</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.drewnoakes</groupId>
+            <artifactId>metadata-extractor</artifactId>
+            <version>2.19.0</version>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 161 - 0
Backend/sample/src/main/java/com/dji/sample/common/util/CoordinateUtil.java

@@ -0,0 +1,161 @@
+package com.dji.sample.common.util;
+
+
+import com.dji.sample.manage.model.common.AMap;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.springframework.stereotype.Component;
+
+import java.text.DecimalFormat;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/7/2
+ */
+
+@Slf4j
+@Component
+public class CoordinateUtil {
+    private static final String GAODE_API_URL = "https://restapi.amap.com/v3/assistant/coordinate/convert?locations=经度,纬度&coordsys=gps&key=96d7df07675b274a4078b107e56b2a4a";// + AMapProperties.key;
+
+    public static void main(String[] args) {
+        double lon = 116.481499;
+        double lat = 39.990475;
+        log.info("GPS转高德之前:" + lon + "," + lat);
+        AMap aMap = CoordinateUtil.convertToAMapCoord(lon,lat);
+        log.info("GPS转高德之后(WEBAPI):" + aMap.getLongitude() + "," + aMap.getLatitude());
+        aMap = transform(lon,lat);
+        log.info("GPS转高德之后:" + aMap.getLongitude() + "," + aMap.getLatitude());
+        /**
+         * 高德API(https://lbs.amap.com/api/webservice/guide/api/convert)经纬度转换之后.
+         * 两者误差不是很大
+         */
+    }
+
+    public static AMap convertToAMapCoord(double originalLng, double originalLat) {
+        String response = sendRequest(originalLng,originalLat);
+        log.info("GPS转高德之后(WEBAPI):" + response);
+        JsonElement element = JsonParser.parseString(response);
+        String status = element.getAsJsonObject().get("status").getAsString();
+        if("1".equals(status)) {
+            String locations = element.getAsJsonObject().get("locations").getAsString();
+            String[] locArray =  locations.split(",");
+            double lon = Double.parseDouble(String.format("%.5f", Double.parseDouble(locArray[0])));
+            double lat = Double.parseDouble(String.format("%.5f", Double.parseDouble(locArray[1])));
+            return new AMap(lon, lat);
+        }
+
+        return null;
+
+    }
+
+    private static String sendRequest(double longitude, double latitude) {
+        HttpGet request = new HttpGet(GAODE_API_URL
+                .replace("经度", Double.toString(longitude))
+                .replace("纬度", Double.toString(latitude)));
+
+        try {
+            org.apache.http.client.HttpClient client = HttpClients.createDefault();
+            HttpResponse httpResponse = client.execute(request);
+            return EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 椭球参数
+     */
+    private static double pi = 3.14159265358979324;
+    /**
+     * 卫星椭球坐标投影到平面地图坐标系的投影因子
+     */
+    private static double a = 6378245.0;
+    /**
+     * 椭球的偏心率
+     */
+    private static double ee = 0.00669342162296594323;
+
+    /**
+     * 经纬度 GPS转高德
+     *
+     * @param wgLon GPS经度
+     * @param wgLat GPS维度
+     * @return 转化后的经纬度坐标
+     */
+    public static AMap transform(double wgLon, double wgLat) {
+        if (outOfChina(wgLat, wgLon)) {
+            return new AMap(wgLon, wgLat);
+        }
+
+        double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
+        double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
+        double radLat = wgLat / 180.0 * 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) * pi);
+        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
+        double transLat = wgLat + dLat;
+        double transLon = wgLon + dLon;
+
+        DecimalFormat df = new DecimalFormat("#.00000");
+        transLon = Double.parseDouble(df.format(transLon));
+        transLat = Double.parseDouble(df.format(transLat));
+
+
+        return new AMap(transLon, transLat);
+    }
+
+    /**
+     * 判断是否为国外坐标,,不在国内不做偏移
+     *
+     * @param lat
+     * @param lon
+     * @return
+     */
+    private static Boolean outOfChina(double lat, double lon) {
+        if (lon < 72.004 || lon > 137.8347)
+            return true;
+        if (lat < 0.8293 || lat > 55.8271)
+            return true;
+        return false;
+    }
+
+    /**
+     * 纬度转换
+     *
+     * @param x
+     * @param y
+     * @return
+     */
+    private static double transformLat(double x, double y) {
+        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
+        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
+        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
+        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
+        return ret;
+    }
+
+    /**
+     * 经度转换
+     *
+     * @param x
+     * @param y
+     * @return
+     */
+    private static double transformLon(double x, double y) {
+        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
+        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
+        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
+        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
+        return ret;
+    }
+}

+ 61 - 0
Backend/sample/src/main/java/com/dji/sample/common/util/PicExifUtil.java

@@ -0,0 +1,61 @@
+package com.dji.sample.common.util;
+
+
+import com.dji.sample.component.oss.model.OssConfiguration;
+import com.dji.sample.component.oss.service.impl.OssServiceContext;
+import com.dji.sample.manage.model.common.AMap;
+import com.dji.sample.media.model.MediaExifDTO;
+import com.drew.imaging.ImageMetadataReader;
+import com.drew.metadata.Metadata;
+import com.drew.metadata.exif.*;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.io.InputStream;
+import java.text.DecimalFormat;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/7/3
+ */
+
+@Slf4j
+@Component
+public class PicExifUtil {
+
+    public static MediaExifDTO getPicExif(String objectKey) {
+        try {
+            MediaExifDTO.MediaExifDTOBuilder builder = MediaExifDTO.builder();
+            OssServiceContext ossService = SpringBeanUtilsTest.getBean(OssServiceContext.class);
+            InputStream inputStream = ossService.getObject(OssConfiguration.bucket, objectKey);
+            Metadata metadata = ImageMetadataReader.readMetadata(inputStream);
+
+            ExifIFD0Directory exifIFD0Directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
+            ExifSubIFDDirectory exifSubIFDDirectory = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
+            GpsDirectory gpsDirectory = metadata.getFirstDirectoryOfType(GpsDirectory.class);
+
+            ExifIFD0Descriptor exifIFD0Descriptor = new ExifIFD0Descriptor(exifIFD0Directory);
+            builder.xResolution(Integer.valueOf(exifIFD0Descriptor.getXResolutionDescription().split(" ")[0]));
+            builder.yResolution(Integer.valueOf(exifIFD0Descriptor.getYResolutionDescription().split(" ")[0]));
+
+            ExifSubIFDDescriptor exifSubIFDDescriptor = new ExifSubIFDDescriptor(exifSubIFDDirectory);
+            builder.imageWidth(Integer.valueOf(exifSubIFDDescriptor.getExifImageWidthDescription().split(" ")[0]));
+            builder.imageHeight(Integer.valueOf(exifSubIFDDescriptor.getExifImageHeightDescription().split(" ")[0]));
+            return builder.build();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+}

+ 0 - 2
Backend/sample/src/main/java/com/dji/sample/common/util/UserRequest.java

@@ -1,8 +1,6 @@
 package com.dji.sample.common.util;
 
 import com.dji.sample.common.model.CustomClaim;
-import com.dji.sample.component.AuthInterceptor;
-import org.springframework.http.server.ServletServerHttpRequest;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
 

+ 2 - 0
Backend/sample/src/main/java/com/dji/sample/component/oss/service/IOssService.java

@@ -48,4 +48,6 @@ public interface IOssService {
     void putObject(String bucket, String objectKey, InputStream input);
 
     void createClient();
+
+    Long getObjectSize(String bucket, String objectKey);
 }

+ 5 - 0
Backend/sample/src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java

@@ -105,4 +105,9 @@ public class AliyunOssServiceImpl implements IOssService {
         this.ossClient = new OSSClientBuilder()
                 .build(OssConfiguration.endpoint, OssConfiguration.accessKey, OssConfiguration.secretKey);
     }
+
+    @Override
+    public Long getObjectSize(String bucket, String objectKey) {
+        return null;
+    }
 }

+ 5 - 0
Backend/sample/src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java

@@ -100,6 +100,11 @@ public class AmazonS3ServiceImpl implements IOssService {
                 .build();
     }
 
+    @Override
+    public Long getObjectSize(String bucket, String objectKey) {
+        return null;
+    }
+
     /**
      * Configuring cross-origin resource sharing
      */

+ 16 - 0
Backend/sample/src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java

@@ -120,4 +120,20 @@ public class MinIOServiceImpl implements IOssService {
                 .region(OssConfiguration.region)
                 .build();
     }
+
+    @Override
+    public Long getObjectSize(String bucket, String objectKey) {
+        // 获取对象的元信息
+        try {
+            StatObjectResponse statObjectResponse = this.client.statObject(
+                    StatObjectArgs.builder()
+                            .bucket(bucket)
+                            .object(objectKey)
+                            .build()
+            );
+            return statObjectResponse.size();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
 }

+ 4 - 0
Backend/sample/src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java

@@ -65,4 +65,8 @@ public class OssServiceContext {
     void createClient() {
         this.ossService.createClient();
     }
+
+    public Long getObjectSize(String bucket, String objectKey) {
+        return this.ossService.getObjectSize(bucket,objectKey);
+    }
 }

+ 1 - 1
Backend/sample/src/main/java/com/dji/sample/manage/controller/DeviceOprLogsController.java

@@ -46,7 +46,7 @@ public class DeviceOprLogsController {
             for(DeviceOprLogsDTO logsDTO : data.getList()) {
                 Optional<DeviceDTO> deviceOpt = deviceService.getDeviceBySn(logsDTO.getDeviceSn());
                 if(deviceOpt.isPresent()) {
-                    logsDTO.setChildren(deviceService.setChild(deviceOpt.get()));
+                    logsDTO.setChildren(deviceService.setChild(deviceOpt.get()).getChildren());
                 }
             }
         }

+ 32 - 0
Backend/sample/src/main/java/com/dji/sample/manage/model/common/AMap.java

@@ -0,0 +1,32 @@
+package com.dji.sample.manage.model.common;
+
+/**
+ * 高德经纬度类
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/7/2
+ */
+public class AMap {
+    /**
+     * 经度
+     */
+    private double longitude;
+
+    /**
+     * 维度
+     */
+    private double latitude;
+
+    public AMap(double longitude, double latitude) {
+        this.longitude = longitude;
+        this.latitude = latitude;
+    }
+
+    public double getLongitude() {
+        return longitude;
+    }
+
+    public double getLatitude() {
+        return latitude;
+    }
+}

+ 25 - 0
Backend/sample/src/main/java/com/dji/sample/manage/model/common/AMapProperties.java

@@ -0,0 +1,25 @@
+package com.dji.sample.manage.model.common;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author sean
+ * @version 1.3
+ * @date 2022/11/10
+ */
+@Component
+@ConfigurationProperties(prefix = "amap")
+public class AMapProperties {
+
+    public static String key;
+
+    public static String type;
+
+    public void setKey(String key) {
+        AMapProperties.key = key;
+    }
+    public void setType(String type) {
+        AMapProperties.type = type;
+    }
+}

+ 3 - 0
Backend/sample/src/main/java/com/dji/sample/manage/model/dto/DeviceDTO.java

@@ -1,6 +1,7 @@
 package com.dji.sample.manage.model.dto;
 
 import com.dji.sample.manage.model.enums.DeviceFirmwareStatusEnum;
+import com.dji.sample.manage.model.enums.DeviceStatusEnum;
 import com.dji.sdk.cloudapi.device.ControlSourceEnum;
 import com.dji.sdk.cloudapi.device.DeviceDomainEnum;
 import com.dji.sdk.cloudapi.device.DeviceSubTypeEnum;
@@ -67,6 +68,8 @@ public class DeviceDTO {
 
     private DeviceFirmwareStatusEnum firmwareStatus;
 
+    private DeviceStatusEnum deviceStatus;
+
     private Integer firmwareProgress;
 
     private String parentSn;

+ 105 - 0
Backend/sample/src/main/java/com/dji/sample/manage/model/enums/DeviceStatusEnum.java

@@ -0,0 +1,105 @@
+package com.dji.sample.manage.model.enums;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/7/2
+ */
+public enum DeviceStatusEnum {
+
+    /**
+     * 飞行器未连接
+     */
+    DRONE_DISCONNECTED(0,"飞行器未连接"),
+
+    /**
+     *  飞行器已连接
+     */
+    DRONE_CONNECTED(1,"飞行器已连接"),
+
+    /**
+     * 飞行器待机
+     */
+    DRONE_STANDBY(3,"待机"),
+
+    /**
+     * 飞行器手动飞行
+     */
+    DRONE_FLY_BY_HAND(4,"手动飞行"),
+
+    /**
+     * 飞行器航线飞行
+     */
+    DRONE_FLY_WAYLINE(5,"航线飞行"),
+
+    /**
+     * 飞行器 舱内关机
+     */
+    DRONE_SHUTDOWN_IN_DOCK(6,"舱内关机"),
+
+    /**
+     * 遥控器 设备已离线
+     */
+    REMOTER_CONTROL_DISCONNECTED(20,"设备已离线"),
+
+    /**
+     * 遥控器 设备空闲中
+     */
+    REMOTER_CONTROL_STANDBY(21,"设备空闲中"),
+
+    /**
+     * 遥控器 飞行作业中
+     */
+    REMOTER_CONTROL_FLYING(22,"飞行作业中"),
+
+    /**
+     * 机场 设备已离线
+     */
+    DOCK_DISCONNECTED(30,"设备已离线"),
+
+    /**
+     * 机场 设备空闲中
+     */
+    DOCK_STANDBY(31,"设备空闲中"),
+
+    /**
+     * 机场 飞行作业中
+     */
+    DOCK_FLYING(32,"飞行作业中"),
+
+    UNKNOWN(-1,"N/A");
+
+    int val;
+
+    String text;
+
+    @JsonValue
+    public int getVal() {
+        return val;
+    }
+
+    @JsonValue
+    public String getText() {
+        return text;
+    }
+
+    DeviceStatusEnum(int val,String text) {
+        this.val = val;
+        this.text = text;
+    }
+
+    @JsonCreator
+    public static DeviceStatusEnum find(int val) {
+        return Arrays.stream(DeviceStatusEnum.values())
+                .filter(firmwareStatus -> firmwareStatus.val == val)
+                .findFirst().orElse(UNKNOWN);
+    }
+
+
+}

+ 71 - 0
Backend/sample/src/main/java/com/dji/sample/media/controller/FileController.java

@@ -1,15 +1,25 @@
 package com.dji.sample.media.controller;
 
+import com.dji.sample.common.model.CustomClaim;
+import com.dji.sample.manage.model.dto.DeviceDTO;
+import com.dji.sample.media.model.MediaDirDTO;
 import com.dji.sample.media.model.MediaFileDTO;
 import com.dji.sample.media.service.IFileService;
+import com.dji.sample.media.service.IMediaDirService;
 import com.dji.sdk.common.HttpResultResponse;
 import com.dji.sdk.common.PaginationData;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
+import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.net.URL;
+import java.util.Objects;
+import java.util.Optional;
+
+import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;
 
 /**
  * @author sean
@@ -23,6 +33,24 @@ public class FileController {
     @Autowired
     private IFileService fileService;
 
+    @Autowired
+    private IMediaDirService dirService;
+
+    /**
+     * 获取工作空间下的所有目录信息
+     * @param workspaceId
+     * @return
+     */
+    @GetMapping("/{workspace_id}/dirs")
+    public HttpResultResponse<PaginationData<MediaDirDTO>> getDirList(@RequestBody MediaDirDTO mediaDirDTO,
+                                                                         @RequestParam(defaultValue = "1") Long page,
+                                                                         @RequestParam(name = "page_size", defaultValue = "10") Long pageSize,
+                                                                         @PathVariable(name = "workspace_id") String workspaceId
+    ) {
+        PaginationData<MediaDirDTO> dirList = dirService.getMediaDirsPaginationByWorkspaceId(workspaceId, mediaDirDTO,page, pageSize);
+        return HttpResultResponse.success(dirList);
+    }
+
     /**
      * Get information about all the media files in this workspace based on the workspace id.
      * @param workspaceId
@@ -36,6 +64,20 @@ public class FileController {
         return HttpResultResponse.success(filesList);
     }
 
+    /**
+     * 获取文件夹下的文件
+     * @param workspaceId
+     * @return
+     */
+    @GetMapping("/{workspace_id}/files/{dir_id}")
+    public HttpResultResponse<PaginationData<MediaFileDTO>> getFilesList(@RequestParam(defaultValue = "1") Long page,
+                                                                         @RequestParam(name = "page_size", defaultValue = "10") Long pageSize,
+                                                                         @PathVariable(name = "workspace_id") String workspaceId,
+                                                                         @PathVariable(name = "dir_id") String dirId) {
+        PaginationData<MediaFileDTO> filesList = fileService.getMediaFilesPaginationByWorkspaceId(dirId, page, pageSize);
+        return HttpResultResponse.success(filesList);
+    }
+
     /**
      * Query the download address of the file according to the media file id,
      * and redirect to this address directly for download.
@@ -54,4 +96,33 @@ public class FileController {
             e.printStackTrace();
         }
     }
+
+    /**
+     * 查询文件详情
+     * @param workspaceId
+     * @param fileId
+     */
+    @GetMapping("/{workspace_id}/file/{file_id}")
+    public HttpResultResponse getDevice(@PathVariable("workspace_id") String workspaceId,
+                                        @PathVariable("file_id") String fileId) {
+        MediaFileDTO mediaFileDTO = fileService.getMediaFileByFileId(workspaceId,fileId);
+        return mediaFileDTO == null ? HttpResultResponse.error("文件不存在") : HttpResultResponse.success(mediaFileDTO);
+    }
+
+    /**
+     * 上传合成文件
+     * @param file
+     * @return
+     */
+    @PostMapping("/{workspace_id}/file/{dir_id}/upload")
+    public HttpResultResponse uploadUnitedPictures(HttpServletRequest request, MultipartFile file,@PathVariable("dir_id") String dirId) {
+        if (Objects.isNull(file)) {
+            return HttpResultResponse.error("没有文件信息");
+        }
+        CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
+        String workspaceId = customClaim.getWorkspaceId();
+        String creator = customClaim.getUsername();
+        fileService.uploadMediaFile(file, workspaceId,dirId, creator);
+        return HttpResultResponse.success();
+    }
 }

+ 12 - 0
Backend/sample/src/main/java/com/dji/sample/media/dao/IMediaDirMapper.java

@@ -0,0 +1,12 @@
+package com.dji.sample.media.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.dji.sample.media.model.MediaDirEntity;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/7/2
+ */
+public interface IMediaDirMapper extends BaseMapper<MediaDirEntity> {
+}

+ 44 - 0
Backend/sample/src/main/java/com/dji/sample/media/model/MediaDirDTO.java

@@ -0,0 +1,44 @@
+package com.dji.sample.media.model;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * @author sean
+ * @version 0.2
+ * @date 2021/12/9
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class MediaDirDTO {
+
+    private Integer id;
+
+    private String workspaceId;
+
+    private String dirName;
+
+    private Integer parentId;
+
+    private String deviceName;
+
+    private String payload;
+
+    private String waylineName;
+
+    private String templateType;
+
+    private LocalDateTime createTime;
+
+    private String username;
+
+    private List<MediaDirDTO> children;
+}

+ 53 - 0
Backend/sample/src/main/java/com/dji/sample/media/model/MediaDirEntity.java

@@ -0,0 +1,53 @@
+package com.dji.sample.media.model;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/7/2
+ */
+@TableName(value = "media_dir")
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class MediaDirEntity implements Serializable {
+
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @TableField("workspace_id")
+    private String workspaceId;
+
+    @TableField("dir_name")
+    private String dirName;
+
+    @TableField("parent_id")
+    private Integer parentId;
+
+    @TableField("device_name")
+    private String deviceName;
+
+    @TableField("payload")
+    private String payload;
+
+    @TableField("wayline_name")
+    private String waylineName;
+
+    @TableField("template_type")
+    private String templateType;
+
+    @TableField("create_time")
+    private Long createTime;
+
+    @TableField("username")
+    private String username;
+}
+

+ 29 - 0
Backend/sample/src/main/java/com/dji/sample/media/model/MediaExifDTO.java

@@ -0,0 +1,29 @@
+package com.dji.sample.media.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * @author sean
+ * @version 0.2
+ * @date 2021/12/9
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class MediaExifDTO {
+
+    private Integer xResolution;
+
+    private Integer yResolution;
+
+    private Integer imageWidth;
+
+    private Integer imageHeight;
+}

+ 33 - 0
Backend/sample/src/main/java/com/dji/sample/media/model/MediaFileDTO.java

@@ -1,10 +1,12 @@
 package com.dji.sample.media.model;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
 /**
@@ -41,4 +43,35 @@ public class MediaFileDTO {
     private LocalDateTime createTime;
 
     private String jobId;
+
+    private Integer dirId;
+
+    private String waylineName;
+
+    private Long size;
+
+    private BigDecimal longitude;
+
+    private BigDecimal latitude;
+
+    private Integer imageWidth;
+
+    private Integer imageHeight;
+
+    private BigDecimal absoluteAltitude;
+
+    private BigDecimal gimbalYawDegree;
+
+    private BigDecimal relativeAltitude;
+
+    private String taskName;
+
+    private String sourceFrom;
+
+    private LocalDateTime pictureTime;
+
+    private Integer xResolution;
+
+    private Integer yResolution;
+
 }

+ 47 - 0
Backend/sample/src/main/java/com/dji/sample/media/model/MediaFileEntity.java

@@ -7,6 +7,7 @@ import lombok.Data;
 import lombok.NoArgsConstructor;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
 
 /**
  * @author sean
@@ -64,5 +65,51 @@ public class MediaFileEntity implements Serializable {
 
     @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
     private Long updateTime;
+
+    @TableField("dir_id")
+    private Integer dirId;
+
+    @TableField("wayline_name")
+    private String waylineName;
+
+    @TableField("size")
+    private Long size;
+
+    @TableField("longitude")
+    private BigDecimal longitude;
+
+    @TableField("latitude")
+    private BigDecimal latitude;
+
+    @TableField("image_width")
+    private Integer imageWidth;
+
+    @TableField("image_height")
+    private Integer imageHeight;
+
+    @TableField("absolute_altitude")
+    private BigDecimal absoluteAltitude;
+
+    @TableField("gimbal_yaw_degree")
+    private BigDecimal gimbalYawDegree;
+
+    @TableField("relative_altitude")
+    private BigDecimal relativeAltitude;
+
+    @TableField("task_name")
+    private String taskName;
+
+    @TableField("source_from")
+    private String sourceFrom;
+
+    @TableField("picture_time")
+    private Long pictureTime;
+
+    @TableField("x_resolution")
+    private Integer xResolution;
+
+    @TableField("y_resolution")
+    private Integer yResolution;
+
 }
 

+ 14 - 0
Backend/sample/src/main/java/com/dji/sample/media/service/IFileService.java

@@ -3,6 +3,7 @@ package com.dji.sample.media.service;
 import com.dji.sample.media.model.MediaFileDTO;
 import com.dji.sdk.cloudapi.media.MediaUploadCallbackRequest;
 import com.dji.sdk.common.PaginationData;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.net.URL;
 import java.util.List;
@@ -14,6 +15,8 @@ import java.util.List;
  */
 public interface IFileService {
 
+    MediaFileDTO getMediaFileByFileId(String workspaceId, String fileId);
+
     /**
      * Query if the file already exists based on the workspace id and the fingerprint of the file.
      * @param workspaceId
@@ -22,6 +25,8 @@ public interface IFileService {
      */
     Boolean checkExist(String workspaceId, String fingerprint);
 
+    void uploadMediaFile(MultipartFile file, String workspaceId, String dirId, String creator);
+
     /**
      * Save the basic information of the file to the database.
      * @param workspaceId
@@ -61,4 +66,13 @@ public interface IFileService {
      * @return
      */
     List<MediaFileDTO> getFilesByWorkspaceAndJobId(String workspaceId, String jobId);
+
+    /**
+     * 通过文件夹ID获取文件
+     * @param dirId
+     * @param page
+     * @param pageSize
+     * @return
+     */
+    PaginationData<MediaFileDTO> getMediaFilesPaginationByDirId(String dirId, long page, long pageSize);
 }

+ 47 - 0
Backend/sample/src/main/java/com/dji/sample/media/service/IMediaDirService.java

@@ -0,0 +1,47 @@
+package com.dji.sample.media.service;
+
+import com.dji.sample.media.model.MediaDirDTO;
+import com.dji.sample.media.model.MediaDirEntity;
+import com.dji.sample.media.model.MediaFileDTO;
+import com.dji.sdk.cloudapi.media.MediaUploadCallbackRequest;
+import com.dji.sdk.common.PaginationData;
+
+import java.net.URL;
+import java.util.List;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/7/2
+ */
+public interface IMediaDirService {
+
+    PaginationData<MediaDirDTO> getMediaDirsPaginationByWorkspaceId(String workspaceId, MediaDirDTO mediaDirDTO, long page, long pageSize);
+
+    /**
+     * 通过目录名称查找目录
+     * @param workspaceId
+     * @param dirName
+     * param  parentId
+     * @return
+     */
+    MediaDirDTO getMediaDirByName(String workspaceId, String dirName, Integer parentId);
+
+    MediaDirDTO getMediaDirById(String workspaceId, String dirId);
+
+    /**
+     * 创建文件夹
+     * @param mediaDirDTO
+     * param  parentId
+     * @return
+     */
+    Integer createDir(MediaDirDTO mediaDirDTO);
+
+    /**
+     * 创建文件夹
+     * @param dirEntity
+     * param  parentId
+     * @return
+     */
+    MediaDirDTO createDir(MediaDirEntity dirEntity);
+}

+ 157 - 12
Backend/sample/src/main/java/com/dji/sample/media/service/impl/FileServiceImpl.java

@@ -2,23 +2,38 @@ package com.dji.sample.media.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.dji.sample.common.util.CoordinateUtil;
+import com.dji.sample.common.util.PicExifUtil;
+import com.dji.sample.common.util.UserRequest;
 import com.dji.sample.component.oss.model.OssConfiguration;
 import com.dji.sample.component.oss.service.impl.OssServiceContext;
+import com.dji.sample.manage.model.common.AMap;
 import com.dji.sample.manage.model.dto.DeviceDictionaryDTO;
 import com.dji.sample.manage.service.IDeviceDictionaryService;
 import com.dji.sample.media.dao.IFileMapper;
-import com.dji.sample.media.model.MediaFileDTO;
-import com.dji.sample.media.model.MediaFileEntity;
+import com.dji.sample.media.model.*;
 import com.dji.sample.media.service.IFileService;
+import com.dji.sample.media.service.IMediaDirService;
+import com.dji.sample.wayline.service.IWaylineFileService;
 import com.dji.sdk.cloudapi.device.DeviceEnum;
 import com.dji.sdk.cloudapi.media.MediaSubFileTypeEnum;
 import com.dji.sdk.cloudapi.media.MediaUploadCallbackRequest;
+import com.dji.sdk.cloudapi.wayline.GetWaylineListResponse;
 import com.dji.sdk.common.Pagination;
 import com.dji.sdk.common.PaginationData;
+import com.drew.metadata.exif.ExifSubIFDDescriptor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
 
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.net.URL;
 import java.time.Instant;
 import java.time.LocalDateTime;
@@ -33,6 +48,7 @@ import java.util.stream.Collectors;
  * @version 0.2
  * @date 2021/12/9
  */
+@Slf4j
 @Service
 @Transactional
 public class FileServiceImpl implements IFileService {
@@ -46,6 +62,12 @@ public class FileServiceImpl implements IFileService {
     @Autowired
     private OssServiceContext ossService;
 
+    @Autowired
+    private IMediaDirService mediaDirService;
+
+    @Autowired
+    private  IWaylineFileService waylineFileService;
+
     private Optional<MediaFileEntity> getMediaByFingerprint(String workspaceId, String fingerprint) {
         MediaFileEntity fileEntity = mapper.selectOne(new LambdaQueryWrapper<MediaFileEntity>()
                 .eq(MediaFileEntity::getWorkspaceId, workspaceId)
@@ -53,6 +75,14 @@ public class FileServiceImpl implements IFileService {
         return Optional.ofNullable(fileEntity);
     }
 
+    @Override
+    public MediaFileDTO getMediaFileByFileId(String workspaceId, String fileId) {
+        MediaFileEntity fileEntity = mapper.selectOne(new LambdaQueryWrapper<MediaFileEntity>()
+                .eq(MediaFileEntity::getWorkspaceId, workspaceId)
+                .eq(MediaFileEntity::getFileId, fileId));
+       return entityConvertToDto(fileEntity);
+    }
+
     private Optional<MediaFileEntity> getMediaByFileId(String workspaceId, String fileId) {
         MediaFileEntity fileEntity = mapper.selectOne(new LambdaQueryWrapper<MediaFileEntity>()
                 .eq(MediaFileEntity::getWorkspaceId, workspaceId)
@@ -65,9 +95,44 @@ public class FileServiceImpl implements IFileService {
         return this.getMediaByFingerprint(workspaceId, fingerprint).isPresent();
     }
 
+    @Override
+    public void uploadMediaFile(MultipartFile file, String workspaceId, String dirId, String creator) {
+        try (InputStream is = file.getInputStream()) {
+            long size = is.available();
+
+            String fileId = UUID.randomUUID().toString();
+            String objectKey = OssConfiguration.objectDirPrefix + File.separator + file.getOriginalFilename();
+
+            MediaDirDTO dirDto = mediaDirService.getMediaDirById(workspaceId,dirId);
+            if(dirDto == null) {
+                throw new RuntimeException("文件夹不存在 ID:" + dirId);
+            }
+
+            ossService.putObject(OssConfiguration.bucket, objectKey, file.getInputStream());
+            log.info("上传成功. {}", file.getOriginalFilename());
+            MediaFileEntity fileEntity = MediaFileEntity.builder()
+                    .dirId(Integer.valueOf(dirId))
+                    .fileId(fileId)
+                    .fileName(file.getOriginalFilename())
+                    .filePath(dirDto.getDirName())
+                    .workspaceId(workspaceId)
+                    .objectKey(objectKey)
+                    .subFileType(0)
+                    .isOriginal(false)
+                    .createTime(System.currentTimeMillis())
+                    .updateTime(System.currentTimeMillis())
+                    .build();
+
+            mapper.insert(fileEntity);
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+        }
+    }
+
     @Override
     public Integer saveFile(String workspaceId, MediaUploadCallbackRequest file) {
-        MediaFileEntity fileEntity = this.fileUploadConvertToEntity(file);
+        MediaFileEntity fileEntity = this.fileUploadConvertToEntity(workspaceId,file);
         fileEntity.setWorkspaceId(workspaceId);
         fileEntity.setFileId(UUID.randomUUID().toString());
         return mapper.insert(fileEntity);
@@ -116,15 +181,55 @@ public class FileServiceImpl implements IFileService {
                 .map(this::entityConvertToDto).collect(Collectors.toList());
     }
 
+    @Override
+    public PaginationData<MediaFileDTO> getMediaFilesPaginationByDirId(String dirId, long page, long pageSize) {
+        Page<MediaFileEntity> pageData = mapper.selectPage(
+                new Page<MediaFileEntity>(page, pageSize),
+                new LambdaQueryWrapper<MediaFileEntity>()
+                        .eq(MediaFileEntity::getWorkspaceId, dirId)
+                        .orderByDesc(MediaFileEntity::getId));
+        List<MediaFileDTO> records = pageData.getRecords()
+                .stream()
+                .map(this::entityConvertToDto)
+                .collect(Collectors.toList());
+
+        return new PaginationData<MediaFileDTO>(records, new Pagination(pageData.getCurrent(), pageData.getSize(), pageData.getTotal()));
+    }
+
     /**
      * Convert the received file object into a database entity object.
      * @param file
      * @return
      */
-    private MediaFileEntity fileUploadConvertToEntity(MediaUploadCallbackRequest file) {
+    private MediaFileEntity fileUploadConvertToEntity(String workspaceId, MediaUploadCallbackRequest file) {
+        //通过path 获取航线名称,文件夹名称
+        String waylineName = getWaylineName(file.getPath());
+        String dirName = getDirName(waylineName, file.getPath());
+        MediaDirDTO dirDto = mediaDirService.getMediaDirByName(workspaceId,dirName,null);
+        //文件夹不存在,则创建文件夹
+        if(dirDto == null) {
+            Optional<GetWaylineListResponse> waylineOpt = waylineFileService.getWaylineByWaylineName(workspaceId,waylineName);
+            MediaDirEntity.MediaDirEntityBuilder dirBuilder = MediaDirEntity.builder();
+            dirBuilder.dirName(dirName)
+                    .workspaceId(workspaceId)
+                    .waylineName(waylineName)
+                    .createTime(System.currentTimeMillis())
+                    .deviceName(getDeviceNameByModelKey(file.getExt().getDroneModelKey()))
+                    .payload(getDeviceNameByModelKey(file.getExt().getPayloadModelKey()))
+                    .username(UserRequest.getCurrentUser().getUsername());
+            if(waylineOpt.isPresent()) {
+                waylineOpt.ifPresent(wayline -> dirBuilder.templateType(wayline.getTemplateTypes().get(0).name()));
+            }
+            dirDto = mediaDirService.createDir(dirBuilder.build());
+        }
+
         MediaFileEntity.MediaFileEntityBuilder builder = MediaFileEntity.builder();
 
         if (file != null) {
+            //获取图片信息
+            MediaExifDTO exif = PicExifUtil.getPicExif(file.getObjectKey());
+            //经纬度转换
+            AMap aMap = CoordinateUtil.transform(file.getMetadata().getShootPosition().getLng(),file.getMetadata().getShootPosition().getLat());
             builder.fileName(file.getName())
                     .filePath(file.getPath())
                     .fingerprint(file.getFingerprint())
@@ -134,18 +239,58 @@ public class FileServiceImpl implements IFileService {
                     .jobId(file.getExt().getFileGroupId())
                     .drone(file.getExt().getSn())
                     .tinnyFingerprint(file.getExt().getTinnyFingerprint())
-                    .payload(file.getExt().getPayloadModelKey().getDevice());
-
-            // domain-type-subType
-            DeviceEnum payloadModelKey = file.getExt().getPayloadModelKey();
-            Optional<DeviceDictionaryDTO> payloadDict = deviceDictionaryService
-                    .getOneDictionaryInfoByTypeSubType(payloadModelKey.getDomain().getDomain(),
-                            payloadModelKey.getType().getType(), payloadModelKey.getSubType().getSubType());
-            payloadDict.ifPresent(payload -> builder.payload(payload.getDeviceName()));
+                    .payload(getDeviceNameByModelKey(file.getExt().getPayloadModelKey()))
+                    .dirId(dirDto.getId())
+                    .imageWidth(exif.getImageWidth())
+                    .imageHeight(exif.getImageHeight())
+                    .xResolution(exif.getXResolution())
+                    .yResolution(exif.getYResolution())
+                    .latitude(new BigDecimal(aMap.getLatitude()).setScale(6, RoundingMode.DOWN))
+                    .longitude(new BigDecimal(aMap.getLongitude()).setScale(6, RoundingMode.DOWN))
+                    .absoluteAltitude(new BigDecimal(file.getMetadata().getAbsoluteAltitude()).setScale(6, RoundingMode.DOWN))
+                    .relativeAltitude(new BigDecimal(file.getMetadata().getRelativeAltitude()).setScale(6, RoundingMode.DOWN))
+                    .size(ossService.getObjectSize(OssConfiguration.bucket, file.getObjectKey()))
+                    .pictureTime(file.getMetadata().getCreatedTime() != null ? file.getMetadata().getCreatedTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : null)
+            ;
         }
         return builder.build();
     }
 
+    private String getDeviceNameByModelKey(DeviceEnum modelKey) {
+        Optional<DeviceDictionaryDTO> payloadDict = deviceDictionaryService
+                .getOneDictionaryInfoByTypeSubType(modelKey.getDomain().getDomain(),
+                        modelKey.getType().getType(), modelKey.getSubType().getSubType());
+        if(payloadDict.isPresent()) {
+            return payloadDict.get().getDeviceName();
+        }
+        return  "";
+    }
+
+    private String getWaylineName(String path) {
+        if(!StringUtils.isEmpty(path)) {
+            String[] pathSplit = path.split("-");
+            if(pathSplit.length > 1) {
+                return pathSplit[pathSplit.length - 1];
+            }
+        }
+        return "";
+    }
+
+    //path:DJI_202406241424_027_项目编号0001-新建面状航线2
+    private String getDirName(String waylineName,String path) {
+        if(!StringUtils.isEmpty(path)) {
+            String[] pathSplit = path.split("_");
+            String datetime = pathSplit[1];
+            return (waylineName + " " + datetime.substring(0,4)+ "-"
+                    + datetime.substring(4,6)+ "-"
+                    + datetime.substring(6,8)+ "-"
+                    + " "
+                    + datetime.substring(8,10)+ ":"
+                    + datetime.substring(10,12)).trim();
+        }
+        return "temp";
+    }
+
     /**
      * Convert database entity objects into file data transfer object.
      * @param entity

+ 147 - 0
Backend/sample/src/main/java/com/dji/sample/media/service/impl/MediaDirServiceImpl.java

@@ -0,0 +1,147 @@
+package com.dji.sample.media.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.dji.sample.media.dao.IMediaDirMapper;
+import com.dji.sample.media.model.MediaDirDTO;
+import com.dji.sample.media.model.MediaDirEntity;
+import com.dji.sample.media.model.MediaFileDTO;
+import com.dji.sample.media.model.MediaFileEntity;
+import com.dji.sample.media.service.IMediaDirService;
+import com.dji.sdk.common.Pagination;
+import com.dji.sdk.common.PaginationData;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/7/2
+ */
+@Service
+@Transactional
+public class MediaDirServiceImpl implements IMediaDirService {
+
+    @Autowired
+    private IMediaDirMapper mapper;
+
+    @Override
+    public PaginationData<MediaDirDTO> getMediaDirsPaginationByWorkspaceId(String workspaceId, MediaDirDTO mediaDirDTO, long page, long pageSize) {
+        Page<MediaDirEntity> pageData = mapper.selectPage(
+                new Page<MediaDirEntity>(page, pageSize),
+                new LambdaQueryWrapper<MediaDirEntity>()
+                        .eq(MediaDirEntity::getWorkspaceId, workspaceId)
+                        .orderByDesc(MediaDirEntity::getId));
+        List<MediaDirDTO> records = pageData.getRecords()
+                .stream()
+                .map(this::entityToDto)
+                .collect(Collectors.toList());
+
+        return new PaginationData<MediaDirDTO>(records, new Pagination(pageData.getCurrent(), pageData.getSize(), pageData.getTotal()));
+    }
+
+    @Override
+    public MediaDirDTO getMediaDirByName(String workspaceId, String dirName, Integer parentId) {
+        MediaDirEntity mediaDirEntity = mapper.selectOne(new LambdaQueryWrapper<MediaDirEntity>()
+                .eq(MediaDirEntity::getWorkspaceId, workspaceId)
+                .eq(MediaDirEntity::getDirName, dirName)
+                .eq(MediaDirEntity::getParentId, parentId == null? 0 : parentId));
+        if(mediaDirEntity == null) {
+            return null;
+        }
+       return entityToDto(mediaDirEntity);
+    }
+
+    @Override
+    public MediaDirDTO getMediaDirById(String workspaceId, String dirId) {
+        MediaDirEntity mediaDirEntity = mapper.selectOne(new LambdaQueryWrapper<MediaDirEntity>()
+                .eq(MediaDirEntity::getWorkspaceId, workspaceId)
+                .eq(MediaDirEntity::getId, dirId));
+        return entityToDto(mediaDirEntity);
+    }
+
+    @Override
+    public Integer createDir(MediaDirDTO mediaDirDTO) {
+        MediaDirEntity dirEntity = this.dtoToEntity(mediaDirDTO);
+        return mapper.insert(dirEntity);
+    }
+
+    @Override
+    public MediaDirDTO createDir(MediaDirEntity dirEntity) {
+        int result = mapper.insert(dirEntity);
+        if(result > 0) {
+            return entityToDto(dirEntity);
+        }
+        return null;
+    }
+
+    private MediaDirEntity dtoToEntity(MediaDirDTO mediaDir) {
+        MediaDirEntity.MediaDirEntityBuilder builder = MediaDirEntity.builder();
+
+        if (mediaDir != null) {
+            builder.workspaceId(mediaDir.getWorkspaceId())
+                    .dirName(mediaDir.getDirName())
+                    .parentId(mediaDir.getParentId())
+                    .deviceName(mediaDir.getDeviceName())
+                    .payload(mediaDir.getPayload())
+                    .waylineName(mediaDir.getWaylineName())
+                    .templateType(mediaDir.getTemplateType())
+                    .createTime(mediaDir.getCreateTime() != null ?
+                            mediaDir.getCreateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : null)
+                    .username(mediaDir.getUsername());
+        }
+        return builder.build();
+    }
+
+    private MediaDirDTO entityToDto(MediaDirEntity mediaDirEntity) {
+        MediaDirDTO.MediaDirDTOBuilder builder = MediaDirDTO.builder();
+        if (mediaDirEntity != null) {
+            builder.id(mediaDirEntity.getId())
+                    .workspaceId(mediaDirEntity.getWorkspaceId())
+                    .dirName(mediaDirEntity.getDirName())
+                    .parentId(mediaDirEntity.getParentId())
+                    .deviceName(mediaDirEntity.getDeviceName())
+                    .payload(mediaDirEntity.getPayload())
+                    .waylineName(mediaDirEntity.getWaylineName())
+                    .templateType(mediaDirEntity.getTemplateType())
+                    .createTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(mediaDirEntity.getCreateTime()), ZoneId.systemDefault()))
+                    .username(mediaDirEntity.getUsername());
+        }
+        return builder.build();
+    }
+
+    /**
+     * Convert database entity objects into file data transfer object.
+     * @param entity
+     * @return
+     */
+    private MediaFileDTO entityConvertToDto(MediaFileEntity entity) {
+        MediaFileDTO.MediaFileDTOBuilder builder = MediaFileDTO.builder();
+
+        if (entity != null) {
+            builder.fileName(entity.getFileName())
+                    .fileId(entity.getFileId())
+                    .filePath(entity.getFilePath())
+                    .isOriginal(entity.getIsOriginal())
+                    .fingerprint(entity.getFingerprint())
+                    .objectKey(entity.getObjectKey())
+                    .tinnyFingerprint(entity.getTinnyFingerprint())
+                    .payload(entity.getPayload())
+                    .createTime(LocalDateTime.ofInstant(
+                            Instant.ofEpochMilli(entity.getCreateTime()), ZoneId.systemDefault()))
+                    .drone(entity.getDrone())
+                    .jobId(entity.getJobId());
+
+        }
+
+        return builder.build();
+    }
+
+}

+ 2 - 0
Backend/sample/src/main/java/com/dji/sample/wayline/service/IWaylineFileService.java

@@ -34,6 +34,8 @@ public interface IWaylineFileService {
      */
     Optional<GetWaylineListResponse> getWaylineByWaylineId(String workspaceId, String waylineId);
 
+    Optional<GetWaylineListResponse> getWaylineByWaylineName(String workspaceId, String waylineName);
+
     /**
      * Get the download address of the file object.
      * @param workspaceId

+ 10 - 0
Backend/sample/src/main/java/com/dji/sample/wayline/service/impl/WaylineFileServiceImpl.java

@@ -104,6 +104,16 @@ public class WaylineFileServiceImpl implements IWaylineFileService {
                                     .eq(WaylineFileEntity::getWaylineId, waylineId))));
     }
 
+    @Override
+    public Optional<GetWaylineListResponse> getWaylineByWaylineName(String workspaceId, String waylineName) {
+        return Optional.ofNullable(
+                this.entityConvertToDTO(
+                        mapper.selectOne(
+                                new LambdaQueryWrapper<WaylineFileEntity>()
+                                        .eq(WaylineFileEntity::getWorkspaceId, workspaceId)
+                                        .eq(WaylineFileEntity::getName, waylineName))));
+    }
+
     @Override
     public URL getObjectUrl(String workspaceId, String waylineId) throws SQLException {
         Optional<GetWaylineListResponse> waylineOpt = this.getWaylineByWaylineId(workspaceId, waylineId);

+ 5 - 0
Backend/sample/src/main/resources/application.yml

@@ -125,6 +125,11 @@ oss:
   region: us-east-1
   object-dir-prefix: wayline
 
+# 高德地图 trans: CUSTOM 自定义  GAODE 高德API
+amap:
+  key: 96d7df07675b274a4078b107e56b2a4a
+  type: CUSTOM
+
 logging:
   level:
     com.dji: debug