sean.zhou
2022-12-12 2d8ded3e77b22e44985265ca4063102662e452c1
initial v1.3.1

Added:
1. Device communication backup link switching.
2. Priority report of the media file uploading.
3. Upload firmware file.
4. HMS updated.

Fixed:
1. Closing the stream prematurely.

Note: There is a change in the structure of the table 'manage_device_firmware'.
55 files modified
11 files added
1 files deleted
39048 ■■■■ changed files
api/Cloud API Demo.postman_collection.json 159 ●●●●● patch | view | raw | blame | history
pom.xml 2 ●●● patch | view | raw | blame | history
sql/cloud_sample.sql 13 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/ApplicationBootInitial.java 5 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/GlobalExceptionHandler.java 6 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/GlobalScheduleService.java 13 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/mqtt/config/MqttMessageChannel.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/mqtt/model/ChannelName.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/mqtt/model/DeviceTopicEnum.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/mqtt/model/EventsMethodEnum.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/oss/model/OssConfiguration.java 46 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/oss/service/IOssService.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java 53 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java 62 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java 38 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/oss/service/impl/OssAspectHandler.java 6 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java 15 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/redis/RedisConst.java 4 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/redis/RedisOpsUtils.java 61 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/websocket/model/BizCodeEnum.java 6 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/component/websocket/service/impl/WebSocketManageServiceImpl.java 22 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/control/model/dto/AlarmState.java 29 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/control/model/dto/BatteryStoreMode.java 4 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/control/model/dto/LinkWorkMode.java 31 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/control/model/enums/LinkWorkModeEnum.java 29 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/control/model/enums/RemoteControlMethodEnum.java 11 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java 20 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/controller/DeviceFirmwareController.java 76 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/dto/DeviceFirmwareDTO.java 6 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/dto/FirmwareFileProperties.java 28 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/entity/DeviceFirmwareEntity.java 10 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/enums/DeviceSetPropertyEnum.java 7 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/enums/StateSwitchEnum.java 18 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/enums/StateSwitchReceiver.java 30 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareQueryParam.java 32 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareUpdateParam.java 17 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareUploadParam.java 23 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/receiver/DistanceLimitStatusReceiver.java 8 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/receiver/HeightLimitReceiver.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/receiver/NightLightsStateReceiver.java 28 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/receiver/ObstacleAvoidanceReceiver.java 14 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/service/IDeviceFirmwareService.java 43 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/service/impl/CapacityCameraServiceImpl.java 11 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/service/impl/DeviceFirmwareServiceImpl.java 188 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/service/impl/DeviceHmsServiceImpl.java 11 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/service/impl/DevicePayloadServiceImpl.java 11 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java 70 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/service/impl/LiveStreamServiceImpl.java 9 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/service/impl/LogsFileServiceImpl.java 7 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/media/controller/FileController.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/media/model/MediaFileCountDTO.java 30 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/media/model/MediaFileDTO.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/media/model/MediaMethodEnum.java 20 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/media/service/IFileService.java 10 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/media/service/IMediaService.java 8 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/media/service/impl/FileServiceImpl.java 19 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/media/service/impl/MediaServiceImpl.java 143 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/storage/service/impl/StorageServiceImpl.java 13 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/wayline/controller/WaylineFileController.java 1 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java 13 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/wayline/model/dto/WaylineJobDTO.java 4 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java 7 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java 24 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/wayline/service/impl/WaylineFileServiceImpl.java 15 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java 73 ●●●● patch | view | raw | blame | history
src/main/resources/application.yml 4 ●●●● patch | view | raw | blame | history
src/main/resources/hms.json 37363 ●●●● patch | view | raw | blame | history
api/Cloud API Demo.postman_collection.json
@@ -761,6 +761,125 @@
                        }
                    },
                    "response": []
                },
                {
                    "name": "Get All Firmwares",
                    "request": {
                        "method": "GET",
                        "header": [],
                        "url": {
                            "raw": "{{base_url}}{{manage_version}}/workspaces/{{workspace_id}}/firmwares?page=1&page_size=50",
                            "host": [
                                "{{base_url}}{{manage_version}}"
                            ],
                            "path": [
                                "workspaces",
                                "{{workspace_id}}",
                                "firmwares"
                            ],
                            "query": [
                                {
                                    "key": "device_name",
                                    "value": null,
                                    "disabled": true
                                },
                                {
                                    "key": "product_version",
                                    "value": null,
                                    "disabled": true
                                },
                                {
                                    "key": "status",
                                    "value": "true",
                                    "disabled": true
                                },
                                {
                                    "key": "page",
                                    "value": "1"
                                },
                                {
                                    "key": "page_size",
                                    "value": "50"
                                }
                            ]
                        }
                    },
                    "response": []
                },
                {
                    "name": "Import Firmware File",
                    "request": {
                        "method": "POST",
                        "header": [],
                        "body": {
                            "mode": "formdata",
                            "formdata": [
                                {
                                    "key": "file",
                                    "type": "file",
                                    "src": []
                                },
                                {
                                    "key": "release_note",
                                    "value": "123",
                                    "type": "text"
                                },
                                {
                                    "key": "device_name",
                                    "value": "DJI Dock",
                                    "type": "text"
                                },
                                {
                                    "key": "status",
                                    "value": "0",
                                    "type": "text"
                                }
                            ]
                        },
                        "url": {
                            "raw": "{{base_url}}{{manage_version}}/workspaces/{{workspace_id}}/firmwares/file/upload",
                            "host": [
                                "{{base_url}}{{manage_version}}"
                            ],
                            "path": [
                                "workspaces",
                                "{{workspace_id}}",
                                "firmwares",
                                "file",
                                "upload"
                            ]
                        }
                    },
                    "response": []
                },
                {
                    "name": "Change Firmware Status",
                    "request": {
                        "method": "PUT",
                        "header": [],
                        "body": {
                            "mode": "raw",
                            "raw": "{\r\n    \"status\": false\r\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        },
                        "url": {
                            "raw": "{{base_url}}{{manage_version}}/workspaces/{{workspace_id}}/firmwares/{{firmware_id}}",
                            "host": [
                                "{{base_url}}{{manage_version}}"
                            ],
                            "path": [
                                "workspaces",
                                "{{workspace_id}}",
                                "firmwares",
                                "{{firmware_id}}"
                            ]
                        }
                    },
                    "response": []
                }
            ],
            "auth": {
@@ -768,7 +887,7 @@
                "apikey": [
                    {
                        "key": "value",
                        "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njc0NzcwNTUsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njc1NjM0NTUsImlhdCI6MTY2NzQ3NzA1NSwidXNlcm5hbWUiOiJhZG1pblBDIn0.VMJ0ZKn895uvkMDjg2fw3p4trVCUx9ltVFKeP7QmYpo",
                        "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NzAzMTU2MDEsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NzA0MDIwMDEsImlhdCI6MTY3MDMxNTYwMSwidXNlcm5hbWUiOiJhZG1pblBDIn0.yh8SkHZVsoIXo_vtlTGNB-ZX92XayalGe_q7mNRVcdI",
                        "type": "string"
                    },
                    {
@@ -1539,6 +1658,27 @@
                        }
                    },
                    "response": []
                },
                {
                    "name": "Set Media Highest",
                    "request": {
                        "method": "POST",
                        "header": [],
                        "url": {
                            "raw": "{{base_url}}{{wayline_version}}/workspaces/{{workspace_id}}/jobs/{{job_id}}/media-highest",
                            "host": [
                                "{{base_url}}{{wayline_version}}"
                            ],
                            "path": [
                                "workspaces",
                                "{{workspace_id}}",
                                "jobs",
                                "{{job_id}}",
                                "media-highest"
                            ]
                        }
                    },
                    "response": []
                }
            ],
            "auth": {
@@ -1585,16 +1725,25 @@
                    "request": {
                        "method": "POST",
                        "header": [],
                        "body": {
                            "mode": "raw",
                            "raw": "{\r\n    \"action\": 0\r\n}",
                            "options": {
                                "raw": {
                                    "language": "json"
                                }
                            }
                        },
                        "url": {
                            "raw": "{{base_url}}{{control_version}}/devices/4TADK7E000000H/jobs/debug_mode_close",
                            "raw": "{{base_url}}{{control_version}}/devices/{{device_sn}}/jobs/alarm_state_switch",
                            "host": [
                                "{{base_url}}{{control_version}}"
                            ],
                            "path": [
                                "devices",
                                "4TADK7E000000H",
                                "{{device_sn}}",
                                "jobs",
                                "debug_mode_close"
                                "alarm_state_switch"
                            ]
                        }
                    },
@@ -1606,7 +1755,7 @@
                "apikey": [
                    {
                        "key": "value",
                        "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njg0MzE5MzQsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njg1MTgzMzQsImlhdCI6MTY2ODQzMTkzNCwidXNlcm5hbWUiOiJhZG1pblBDIn0.QU9xHBeQPHJ2V1vXQcGGWRQ-gYEOWDpaTTXIQga85BU",
                        "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njk2MzMzMzQsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njk3MTk3MzQsImlhdCI6MTY2OTYzMzMzNCwidXNlcm5hbWUiOiJhZG1pblBDIn0.OoIfdpyI5eL6bFm8akq8_stzClQU41YpIJkx6_kxVHU",
                        "type": "string"
                    },
                    {
pom.xml
@@ -11,7 +11,7 @@
    <groupId>com.dji</groupId>
    <artifactId>cloud-api-sample</artifactId>
    <version>1.3.0</version>
    <version>1.3.1</version>
    <name>cloud-api-sample</name>
    <properties>
sql/cloud_sample.sql
@@ -148,12 +148,14 @@
  `firmware_id` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'uuid',
  `file_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined' COMMENT 'The file name of the firmware package, including the file suffix',
  `firmware_version` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'It needs to be formatted according to the official firmware version. 00.00.0000',
  `file_url` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The download address for the firmware package.',
  `object_key` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'The object key of the firmware package in the bucket.',
  `file_size` int NOT NULL COMMENT 'The size of the firmware package.',
  `file_md5` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The md5 of the firmware package.',
  `device_name` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'model of the device. This parameter corresponds to the device name in the device dictionary table.',
  `workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `release_note` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The release note of the firmware package.',
  `release_date` bigint NOT NULL COMMENT 'The release date of the firmware package.',
  `user_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'The name of the creator.',
  `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Availability of the firmware package. 1: available; 0: unavailable',
  `create_time` bigint NOT NULL,
  `update_time` bigint NOT NULL,
@@ -161,15 +163,6 @@
  UNIQUE KEY `UNIQUE_firmware_id` (`firmware_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='Firmware file information';
LOCK TABLES `manage_device_firmware` WRITE;
/*!40000 ALTER TABLE `manage_device_firmware` DISABLE KEYS */;
INSERT INTO `manage_device_firmware` (`id`, `firmware_id`, `file_name`, `firmware_version`, `file_url`, `file_size`, `file_md5`, `device_name`, `release_note`, `release_date`, `status`, `create_time`, `update_time`)
VALUES
    (1,'1','Matrice_M30_Series_UAV_V04.01.00.20_Only_For_Pilot.zip','04.01.0020','https://terra-sz-hc1pro-cloudapi.oss-cn-shenzhen.aliyuncs.com/c0af9fe0d7eb4f35a8fe5b695e4d0b96/docker/Matrice_M30_Series_UAV_V04.01.00.20_Only_For_Pilot.zip',605830726,'601630a5c753cd6665974cc8fd791bf5','Matrice 30','release note',1663232356810,1,1663232356810,1663232356810);
/*!40000 ALTER TABLE `manage_device_firmware` ENABLE KEYS */;
UNLOCK TABLES;
# manage_device_hms
src/main/java/com/dji/sample/component/ApplicationBootInitial.java
@@ -18,9 +18,6 @@
    @Autowired
    private IDeviceService deviceService;
    @Autowired
    private RedisOpsUtils redisOps;
    /**
     * Subscribe to the devices that exist in the redis when the program starts,
     * to prevent the data from being different from the pilot side due to program interruptions.
@@ -31,7 +28,7 @@
    public void run(String... args) throws Exception {
        int start = RedisConst.DEVICE_ONLINE_PREFIX.length();
        redisOps.getAllKeys(RedisConst.DEVICE_ONLINE_PREFIX + "*")
        RedisOpsUtils.getAllKeys(RedisConst.DEVICE_ONLINE_PREFIX + "*")
                .forEach(key -> deviceService.subscribeTopicOnline(key.substring(start)));
    }
src/main/java/com/dji/sample/component/GlobalExceptionHandler.java
@@ -1,6 +1,7 @@
package com.dji.sample.component;
import com.dji.sample.common.model.ResponseResult;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -32,8 +33,9 @@
        return ResponseResult.error("A null object appeared.");
    }
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
    public ResponseResult methodArgumentNotValidExceptionHandler(BindException e) {
        e.printStackTrace();
        return ResponseResult.error(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
    }
src/main/java/com/dji/sample/component/GlobalScheduleService.java
@@ -6,6 +6,7 @@
import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.enums.DeviceDomainEnum;
import com.dji.sample.manage.service.IDeviceService;
import com.dji.sample.wayline.service.IWaylineJobService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
@@ -27,10 +28,10 @@
    private IDeviceService deviceService;
    @Autowired
    private RedisOpsUtils redisOps;
    private IMqttTopicService topicService;
    @Autowired
    private IMqttTopicService topicService;
    private IWaylineJobService waylineJobService;
    /**
     * Check the status of the devices every 30 seconds. It is recommended to use cache.
@@ -39,17 +40,17 @@
    private void deviceStatusListen() {
        int start = RedisConst.DEVICE_ONLINE_PREFIX.length();
        redisOps.getAllKeys(RedisConst.DEVICE_ONLINE_PREFIX + "*").forEach(key -> {
            long expire = redisOps.getExpire(key);
        RedisOpsUtils.getAllKeys(RedisConst.DEVICE_ONLINE_PREFIX + "*").forEach(key -> {
            long expire = RedisOpsUtils.getExpire(key);
            if (expire <= 30) {
                DeviceDTO device = (DeviceDTO) redisOps.get(key);
                DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(key);
                if (device.getDomain().equals(DeviceDomainEnum.SUB_DEVICE.getDesc())) {
                    deviceService.subDeviceOffline(key.substring(start));
                } else {
                    deviceService.unsubscribeTopicOffline(key.substring(start));
                    deviceService.pushDeviceOfflineTopo(device.getWorkspaceId(), device.getDeviceSn());
                }
                redisOps.del(key);
                RedisOpsUtils.del(key);
            }
        });
src/main/java/com/dji/sample/component/mqtt/config/MqttMessageChannel.java
@@ -167,4 +167,9 @@
        return new DirectChannel();
    }
    @Bean(name = ChannelName.INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA)
    public MessageChannel eventsHighestPriorityUploadFlightTaskMedia() {
        return new DirectChannel();
    }
}
src/main/java/com/dji/sample/component/mqtt/model/ChannelName.java
@@ -74,4 +74,6 @@
    public static final String INBOUND_PROPERTY_SET_REPLY = "inboundPropertySetReply";
    public static final String INBOUND_REQUESTS_CONFIG = "inboundRequestsConfig";
    public static final String INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA = "inboundEventsHighestPriorityUploadFlightTaskMedia";
}
src/main/java/com/dji/sample/component/mqtt/model/DeviceTopicEnum.java
@@ -29,7 +29,7 @@
    PROPERTY_SET_REPLY(Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + PROPERTY_SUF + SET_SUF + _REPLY_SUF + "$"), ChannelName.INBOUND_PROPERTY_SET_REPLY),
    UNKNOWN(null, ChannelName.DEFAULT);
    UNKNOWN(Pattern.compile("^.*$"), ChannelName.DEFAULT);
    Pattern pattern;
src/main/java/com/dji/sample/component/mqtt/model/EventsMethodEnum.java
@@ -43,6 +43,8 @@
    FILE_UPLOAD_PROGRESS("fileupload_progress", ChannelName.INBOUND_EVENTS_FILE_UPLOAD_PROGRESS),
    HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA("highest_priority_upload_flighttask_media", ChannelName.INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA),
    UNKNOWN("Unknown", ChannelName.DEFAULT);
    private String method;
src/main/java/com/dji/sample/component/oss/model/OssConfiguration.java
@@ -1,6 +1,5 @@
package com.dji.sample.component.oss.model;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@@ -11,82 +10,81 @@
 */
@ConfigurationProperties(prefix = "oss")
@Component
@Data
public class OssConfiguration {
    /**
     * @see com.dji.sample.component.oss.model.enums.OssTypeEnum
     */
    private String provider;
    public static String provider;
    /**
     * Whether to use the object storage service.
     */
    private boolean enable;
    public static boolean enable;
    /**
     * The protocol needs to be included at the beginning of the address.
     */
    private String endpoint;
    public static String endpoint;
    private String accessKey;
    public static String accessKey;
    private String secretKey;
    public static String secretKey;
    private String region;
    public static String region;
    private Long expire;
    public static Long expire;
    private String roleSessionName;
    public static String roleSessionName;
    private String roleArn;
    public static String roleArn;
    private String bucket;
    public static String bucket;
    private String objectDirPrefix;
    public static String objectDirPrefix;
    public void setProvider(String provider) {
        this.provider = provider;
        OssConfiguration.provider = provider;
    }
    public void setEnable(boolean enable) {
        this.enable = enable;
        OssConfiguration.enable = enable;
    }
    public void setEndpoint(String endpoint) {
        this.endpoint = endpoint;
        OssConfiguration.endpoint = endpoint;
    }
    public void setAccessKey(String accessKey) {
        this.accessKey = accessKey;
        OssConfiguration.accessKey = accessKey;
    }
    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
        OssConfiguration.secretKey = secretKey;
    }
    public void setRegion(String region) {
        this.region = region;
        OssConfiguration.region = region;
    }
    public void setExpire(Long expire) {
        this.expire = expire;
        OssConfiguration.expire = expire;
    }
    public void setRoleSessionName(String roleSessionName) {
        this.roleSessionName = roleSessionName;
        OssConfiguration.roleSessionName = roleSessionName;
    }
    public void setRoleArn(String roleArn) {
        this.roleArn = roleArn;
        OssConfiguration.roleArn = roleArn;
    }
    public void setBucket(String bucket) {
        this.bucket = bucket;
        OssConfiguration.bucket = bucket;
    }
    public void setObjectDirPrefix(String objectDirPrefix) {
        this.objectDirPrefix = objectDirPrefix;
        OssConfiguration.objectDirPrefix = objectDirPrefix;
    }
}
src/main/java/com/dji/sample/component/oss/service/IOssService.java
@@ -45,4 +45,6 @@
    InputStream getObject(String bucket, String objectKey);
    void putObject(String bucket, String objectKey, InputStream input);
    void createClient();
}
src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java
@@ -3,7 +3,6 @@
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
@@ -18,14 +17,12 @@
import com.dji.sample.component.oss.service.IOssService;
import com.dji.sample.media.model.CredentialsDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.Objects;
/**
 * @author sean
@@ -36,9 +33,8 @@
@Slf4j
public class AliyunOssServiceImpl implements IOssService {
    @Autowired
    public OssConfiguration configuration;
    private OSS ossClient;
    @Override
    public String getOssType() {
        return OssTypeEnum.ALIYUN.getType();
@@ -49,16 +45,16 @@
        try {
            DefaultProfile profile = DefaultProfile.getProfile(
                    configuration.getRegion(), configuration.getAccessKey(), configuration.getSecretKey());
                    OssConfiguration.region, OssConfiguration.accessKey, OssConfiguration.secretKey);
            IAcsClient client = new DefaultAcsClient(profile);
            AssumeRoleRequest request = new AssumeRoleRequest();
            request.setDurationSeconds(configuration.getExpire());
            request.setRoleArn(configuration.getRoleArn());
            request.setRoleSessionName(configuration.getRoleSessionName());
            request.setDurationSeconds(OssConfiguration.expire);
            request.setRoleArn(OssConfiguration.roleArn);
            request.setRoleSessionName(OssConfiguration.roleSessionName);
            AssumeRoleResponse response = client.getAcsResponse(request);
            return new CredentialsDTO(response.getCredentials(), configuration.getExpire());
            return new CredentialsDTO(response.getCredentials(), OssConfiguration.expire);
        } catch (ClientException e) {
            log.debug("Failed to obtain sts.");
@@ -69,10 +65,6 @@
    @Override
    public URL getObjectUrl(String bucket, String objectKey) {
        if (!StringUtils.hasText(bucket) || !StringUtils.hasText(objectKey)) {
            return null;
        }
        OSS ossClient = this.createClient();
        // First check if the object can be fetched.
        boolean isExist = ossClient.doesObjectExist(bucket, objectKey);
        if (!isExist) {
@@ -80,50 +72,37 @@
        }
        return ossClient.generatePresignedUrl(bucket, objectKey,
                new Date(System.currentTimeMillis() + configuration.getExpire() * 1000));
                new Date(System.currentTimeMillis() + OssConfiguration.expire * 1000));
    }
    @Override
    public Boolean deleteObject(String bucket, String objectKey) {
        OSS ossClient = this.createClient();
        if (!ossClient.doesObjectExist(bucket, objectKey)) {
            ossClient.shutdown();
            return true;
        }
        ossClient.deleteObject(bucket, objectKey);
        ossClient.shutdown();
        return true;
    }
    @Override
    public InputStream getObject(String bucket, String objectKey) {
        OSS ossClient = this.createClient();
        OSSObject object = ossClient.getObject(bucket, objectKey);
        try (InputStream input = object.getObjectContent()) {
            return input;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            ossClient.shutdown();
        }
        return InputStream.nullInputStream();
        return ossClient.getObject(bucket, objectKey).getObjectContent();
    }
    @Override
    public void putObject(String bucket, String objectKey, InputStream input) {
        OSS ossClient = this.createClient();
        if (ossClient.doesObjectExist(bucket, objectKey)) {
            ossClient.shutdown();
            throw new RuntimeException("The filename already exists.");
        }
        PutObjectResult objectResult = ossClient.putObject(new PutObjectRequest(bucket, objectKey, input, new ObjectMetadata()));
        ossClient.shutdown();
        log.info("Upload File: {}", objectResult.getETag());
    }
    private OSS createClient() {
        return new OSSClientBuilder()
                .build(configuration.getEndpoint(), configuration.getAccessKey(), configuration.getSecretKey());
    public void createClient() {
        if (Objects.nonNull(this.ossClient)) {
            return;
        }
        this.ossClient = new OSSClientBuilder()
                .build(OssConfiguration.endpoint, OssConfiguration.accessKey, OssConfiguration.secretKey);
    }
}
src/main/java/com/dji/sample/component/oss/service/impl/AmazonS3ServiceImpl.java
@@ -17,16 +17,15 @@
import com.dji.sample.component.oss.service.IOssService;
import com.dji.sample.media.model.CredentialsDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
 * @author sean
@@ -37,9 +36,8 @@
@Service
public class AmazonS3ServiceImpl implements IOssService {
    @Autowired
    private OssConfiguration configuration;
    private AmazonS3 client;
    @Override
    public String getOssType() {
        return OssTypeEnum.AWS.getType();
@@ -49,71 +47,55 @@
    public CredentialsDTO getCredentials() {
        AWSSecurityTokenService stsClient = AWSSecurityTokenServiceClientBuilder.standard()
                .withCredentials(new AWSStaticCredentialsProvider(
                        new BasicAWSCredentials(configuration.getAccessKey(), configuration.getSecretKey())))
                .withRegion(configuration.getRegion()).build();
                        new BasicAWSCredentials(OssConfiguration.accessKey, OssConfiguration.secretKey)))
                .withRegion(OssConfiguration.region).build();
        AssumeRoleRequest request = new AssumeRoleRequest()
                .withRoleArn(configuration.getRoleArn())
                .withRoleSessionName(configuration.getRoleSessionName())
                .withDurationSeconds(Math.toIntExact(configuration.getExpire()));
                .withRoleArn(OssConfiguration.roleArn)
                .withRoleSessionName(OssConfiguration.roleSessionName)
                .withDurationSeconds(Math.toIntExact(OssConfiguration.expire));
        AssumeRoleResult result = stsClient.assumeRole(request);
        Credentials credentials = result.getCredentials();
        stsClient.shutdown();
        return new CredentialsDTO(credentials);
    }
    @Override
    public URL getObjectUrl(String bucket, String objectKey) {
        AmazonS3 client = this.createClient();
        URL url = client.generatePresignedUrl(bucket, objectKey,
                new Date(System.currentTimeMillis() + configuration.getExpire() * 1000), HttpMethod.GET);
        client.shutdown();
        return url;
        return client.generatePresignedUrl(bucket, objectKey,
                new Date(System.currentTimeMillis() + OssConfiguration.expire * 1000), HttpMethod.GET);
    }
    @Override
    public Boolean deleteObject(String bucket, String objectKey) {
        AmazonS3 client = this.createClient();
        if (!client.doesObjectExist(bucket, objectKey)) {
            client.shutdown();
            return true;
        }
        client.deleteObject(bucket, objectKey);
        client.shutdown();
        return true;
    }
    public InputStream getObject(String bucket, String objectKey) {
        AmazonS3 client = this.createClient();
        S3Object object = client.getObject(bucket, objectKey);
        try (InputStream input = object.getObjectContent().getDelegateStream()) {
            return input;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            client.shutdown();
        }
        return InputStream.nullInputStream();
        return client.getObject(bucket, objectKey).getObjectContent().getDelegateStream();
    }
    @Override
    public void putObject(String bucket, String objectKey, InputStream input) {
        AmazonS3 client = this.createClient();
        if (client.doesObjectExist(bucket, objectKey)) {
            client.shutdown();
            throw new RuntimeException("The filename already exists.");
        }
        PutObjectResult objectResult = client.putObject(new PutObjectRequest(bucket, objectKey, input, new ObjectMetadata()));
        client.shutdown();
        log.info("Upload File: {}", objectResult.toString());
    }
    private AmazonS3 createClient() {
        return AmazonS3ClientBuilder.standard()
    public void createClient() {
        if (Objects.nonNull(this.client)) {
            return;
        }
        this.client = AmazonS3ClientBuilder.standard()
                .withCredentials(
                        new AWSStaticCredentialsProvider(
                                new BasicAWSCredentials(configuration.getAccessKey(), configuration.getSecretKey())))
                .withRegion(configuration.getRegion())
                                new BasicAWSCredentials(OssConfiguration.accessKey, OssConfiguration.secretKey)))
                .withRegion(OssConfiguration.region)
                .build();
    }
@@ -122,7 +104,7 @@
     */
    @PostConstruct
    private void configCORS() {
        if (!configuration.isEnable() || !OssTypeEnum.AWS.getType().equals(configuration.getProvider())) {
        if (!OssConfiguration.enable || !OssTypeEnum.AWS.getType().equals(OssConfiguration.provider)) {
            return;
        }
        List<CORSRule.AllowedMethods> allowedMethods = new ArrayList<>();
@@ -136,10 +118,8 @@
                .withAllowedHeaders(List.of(AuthInterceptor.PARAM_TOKEN))
                .withAllowedMethods(allowedMethods);
        AmazonS3 client = this.createClient();
        client.setBucketCrossOriginConfiguration(this.configuration.getBucket(),
        client.setBucketCrossOriginConfiguration(OssConfiguration.bucket,
                new BucketCrossOriginConfiguration().withRules(rule));
        client.shutdown();
    }
}
src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java
@@ -9,7 +9,6 @@
import io.minio.errors.*;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
@@ -30,10 +29,7 @@
public class MinIOServiceImpl implements IOssService {
    private MinioClient client;
    @Autowired
    private OssConfiguration configuration;
    @Override
    public String getOssType() {
        return OssTypeEnum.MINIO.getType();
@@ -42,10 +38,10 @@
    @Override
    public CredentialsDTO getCredentials() {
        try {
            AssumeRoleProvider provider = new AssumeRoleProvider(configuration.getEndpoint(), configuration.getAccessKey(),
                    configuration.getSecretKey(), Math.toIntExact(configuration.getExpire()),
                    null, configuration.getRegion(), null, null, null, null);
            return new CredentialsDTO(provider.fetch(), configuration.getExpire());
            AssumeRoleProvider provider = new AssumeRoleProvider(OssConfiguration.endpoint, OssConfiguration.accessKey,
                    OssConfiguration.secretKey, Math.toIntExact(OssConfiguration.expire),
                    null, OssConfiguration.region, null, null, null, null);
            return new CredentialsDTO(provider.fetch(), OssConfiguration.expire);
        } catch (NoSuchAlgorithmException e) {
            log.debug("Failed to obtain sts.");
            e.printStackTrace();
@@ -57,26 +53,22 @@
    public URL getObjectUrl(String bucket, String objectKey) {
        try {
            return new URL(
                    this.createClient()
                            .getPresignedObjectUrl(
                    client.getPresignedObjectUrl(
                                    GetPresignedObjectUrlArgs.builder()
                                            .method(Method.GET)
                                            .bucket(bucket)
                                            .object(objectKey)
                                            .expiry(Math.toIntExact(configuration.getExpire()))
                                            .expiry(Math.toIntExact(OssConfiguration.expire))
                                            .build()));
        } catch (ErrorResponseException | InsufficientDataException | InternalException |
                InvalidKeyException | InvalidResponseException | IOException |
                NoSuchAlgorithmException | XmlParserException | ServerException e) {
            log.error("The file does not exist on the OssConfiguration.");
            e.printStackTrace();
            throw new RuntimeException("The file does not exist on the OssConfiguration.");
        }
        return null;
    }
    @Override
    public Boolean deleteObject(String bucket, String objectKey) {
        MinioClient client = this.createClient();
        try {
            client.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objectKey).build());
        } catch (MinioException | NoSuchAlgorithmException | IOException | InvalidKeyException e) {
@@ -90,7 +82,7 @@
    @Override
    public InputStream getObject(String bucket, String objectKey) {
        try {
            GetObjectResponse object = this.createClient().getObject(GetObjectArgs.builder().bucket(bucket).object(objectKey).build());
            GetObjectResponse object = client.getObject(GetObjectArgs.builder().bucket(bucket).object(objectKey).build());
            return new ByteArrayInputStream(object.readAllBytes());
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException | XmlParserException e) {
            e.printStackTrace();
@@ -101,7 +93,6 @@
    @Override
    public void putObject(String bucket, String objectKey, InputStream input) {
        try {
            MinioClient client = this.createClient();
            client.statObject(StatObjectArgs.builder().bucket(bucket).object(objectKey).build());
            throw new RuntimeException("The filename already exists.");
        } catch (MinioException | InvalidKeyException | IOException | NoSuchAlgorithmException e) {
@@ -117,15 +108,14 @@
        }
    }
    private MinioClient createClient() {
    public void createClient() {
        if (Objects.nonNull(this.client)) {
            return this.client;
            return;
        }
        this.client = MinioClient.builder()
                .endpoint(configuration.getEndpoint())
                .credentials(configuration.getAccessKey(), configuration.getSecretKey())
                .region(configuration.getRegion())
                .endpoint(OssConfiguration.endpoint)
                .credentials(OssConfiguration.accessKey, OssConfiguration.secretKey)
                .region(OssConfiguration.region)
                .build();
        return this.client;
    }
}
src/main/java/com/dji/sample/component/oss/service/impl/OssAspectHandler.java
@@ -18,16 +18,14 @@
    @Autowired
    private OssServiceContext ossServiceContext;
    @Autowired
    private OssConfiguration configuration;
    @Before("execution(public * com.dji.sample.component.oss.service.impl.OssServiceContext.*(..))")
    public void before() {
        if (!this.configuration.isEnable()) {
        if (!OssConfiguration.enable) {
            throw new IllegalArgumentException("Please enable OssConfiguration.");
        }
        if (this.ossServiceContext.getOssService() == null) {
            throw new IllegalArgumentException("Please check the OssConfiguration configuration.");
        }
        this.ossServiceContext.createClient();
    }
}
src/main/java/com/dji/sample/component/oss/service/impl/OssServiceContext.java
@@ -6,6 +6,7 @@
import com.dji.sample.media.model.CredentialsDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.io.InputStream;
import java.net.URL;
@@ -22,16 +23,13 @@
    private IOssService ossService;
    private OssConfiguration configuration;
    @Autowired
    public OssServiceContext(List<IOssService> ossServices, OssConfiguration configuration) {
        this.configuration = configuration;
        if (!configuration.isEnable()) {
        if (!OssConfiguration.enable) {
            return;
        }
        this.ossService = ossServices.stream()
                .filter(ossService -> ossService.getOssType().equals(configuration.getProvider()))
                .filter(ossService -> ossService.getOssType().equals(OssConfiguration.provider))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("Oss provider is illegal. Optional: " +
                        Arrays.toString(Arrays.stream(OssTypeEnum.values()).map(OssTypeEnum::getType).toArray())));
@@ -46,6 +44,9 @@
    }
    public URL getObjectUrl(String bucket, String objectKey) {
        if (!StringUtils.hasText(bucket) || !StringUtils.hasText(objectKey)) {
            throw new IllegalArgumentException();
        }
        return this.ossService.getObjectUrl(bucket, objectKey);
    }
@@ -60,4 +61,8 @@
    public void putObject(String bucket, String objectKey, InputStream stream) {
        this.ossService.putObject(bucket, objectKey, stream);
    }
    void createClient() {
        this.ossService.createClient();
    }
}
src/main/java/com/dji/sample/component/redis/RedisConst.java
@@ -38,4 +38,8 @@
    public static final String WAYLINE_JOB = "wayline_job";
    public static final String OSD_PREFIX = "osd" + DELIMITER;
    public static final String MEDIA_FILE_PREFIX = "media_file" + DELIMITER;
    public static final String MEDIA_HIGHEST_PRIORITY_PREFIX = "media_highest_priority" + DELIMITER;
}
src/main/java/com/dji/sample/component/redis/RedisOpsUtils.java
@@ -17,8 +17,12 @@
@Component
public class RedisOpsUtils {
    private static RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        RedisOpsUtils.redisTemplate = redisTemplate;
    }
    /**
     * HSET
@@ -26,7 +30,7 @@
     * @param field
     * @param value
     */
    public void hashSet(String key, String field, Object value) {
    public static void hashSet(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }
@@ -36,7 +40,7 @@
     * @param field
     * @return
     */
    public Object hashGet(String key, String field) {
    public static Object hashGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }
@@ -45,7 +49,7 @@
     * @param key
     * @return
     */
    public Set<Object> hashKeys(String key) {
    public static Set<Object> hashKeys(String key) {
        return redisTemplate.opsForHash().keys(key);
    }
@@ -55,7 +59,7 @@
     * @param field
     * @return
     */
    public boolean hashCheck(String key, String field) {
    public static boolean hashCheck(String key, String field) {
        return redisTemplate.opsForHash().hasKey(key, field);
    }
@@ -65,8 +69,17 @@
     * @param fields
     * @return
     */
    public boolean hashDel(String key, Object[] fields) {
    public static boolean hashDel(String key, Object[] fields) {
        return redisTemplate.opsForHash().delete(key, fields) > 0;
    }
    /**
     * HLEN
     * @param key
     * @return
     */
    public static long hashLen(String key) {
        return redisTemplate.opsForHash().size(key);
    }
    /**
@@ -75,7 +88,7 @@
     * @param timeout
     * @return
     */
    public boolean expireKey(String key, long timeout) {
    public static boolean expireKey(String key, long timeout) {
        return redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }
@@ -84,7 +97,7 @@
     * @param key
     * @param value
     */
    public void set(String key, Object value) {
    public static void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }
@@ -93,7 +106,7 @@
     * @param key
     * @return
     */
    public Object get(String key) {
    public static Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }
@@ -103,7 +116,7 @@
     * @param value
     * @param expire
     */
    public void setWithExpire(String key, Object value, long expire) {
    public static void setWithExpire(String key, Object value, long expire) {
        redisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
    }
@@ -112,7 +125,7 @@
     * @param key
     * @return
     */
    public long getExpire(String key) {
    public static long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }
@@ -121,7 +134,7 @@
     * @param key
     * @return
     */
    public boolean checkExist(String key) {
    public static boolean checkExist(String key) {
        return redisTemplate.hasKey(key);
    }
@@ -130,8 +143,8 @@
     * @param key
     * @return
     */
    public boolean del(String key) {
        return this.checkExist(key) && redisTemplate.delete(key);
    public static boolean del(String key) {
        return RedisOpsUtils.checkExist(key) && redisTemplate.delete(key);
    }
    /**
@@ -139,7 +152,7 @@
     * @param pattern
     * @return
     */
    public Set<String> getAllKeys(String pattern) {
    public static Set<String> getAllKeys(String pattern) {
        return redisTemplate.keys(pattern);
    }
@@ -148,7 +161,7 @@
     * @param key
     * @param value
     */
    public void listRPush(String key, Object... value) {
    public static void listRPush(String key, Object... value) {
        if (value.length == 0) {
            return;
        }
@@ -164,7 +177,7 @@
     * @param end
     * @return
     */
    public List<Object> listGet(String key, long start, long end) {
    public static List<Object> listGet(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }
@@ -173,7 +186,7 @@
     * @param key
     * @return
     */
    public List<Object> listGetAll(String key) {
    public static List<Object> listGetAll(String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }
@@ -182,7 +195,7 @@
     * @param key
     * @return
     */
    public Long listLen(String key) {
    public static Long listLen(String key) {
        return redisTemplate.opsForList().size(key);
    }
@@ -192,7 +205,7 @@
     * @param value
     * @param score
     */
    public Boolean zAdd(String key, Object value, double score) {
    public static Boolean zAdd(String key, Object value, double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }
@@ -201,7 +214,7 @@
     * @param key
     * @param value
     */
    public Boolean zRemove(String key, Object... value) {
    public static Boolean zRemove(String key, Object... value) {
        return redisTemplate.opsForZSet().remove(key, value) > 0;
    }
    /**
@@ -211,7 +224,7 @@
     * @param end
     * @return
     */
    public Set<Object> zRange(String key, long start, long end) {
    public static Set<Object> zRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }
@@ -220,7 +233,7 @@
     * @param key
     * @return
     */
    public Object zGetMin(String key) {
    public static Object zGetMin(String key) {
        Set<Object> objects = zRange(key, 0, 0);
        if (CollectionUtils.isEmpty(objects)) {
            return null;
@@ -234,7 +247,7 @@
     * @param value
     * @return
     */
    public Double zScore(String key, Object value) {
    public static Double zScore(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }
src/main/java/com/dji/sample/component/websocket/model/BizCodeEnum.java
@@ -53,7 +53,11 @@
    CHARGE_OPEN("charge_open"),
    CHARGE_CLOSE("charge_close");
    CHARGE_CLOSE("charge_close"),
    FILE_UPLOAD_CALLBACK("file_upload_callback"),
    HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA("HIGHEST_PRIORITY_UPLOAD_FLIGHTTASK_MEDIA");
    private String code;
src/main/java/com/dji/sample/component/websocket/service/impl/WebSocketManageServiceImpl.java
@@ -6,7 +6,6 @@
import com.dji.sample.component.websocket.service.IWebSocketManageService;
import com.dji.sample.manage.model.enums.UserTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@@ -26,10 +25,7 @@
public class WebSocketManageServiceImpl implements IWebSocketManageService {
    private static final ConcurrentHashMap<String, ConcurrentWebSocketSession> SESSIONS = new ConcurrentHashMap<>(16);
    @Autowired
    private RedisOpsUtils redisOps;
    @Override
    public void put(String key, ConcurrentWebSocketSession val) {
        String[] name = key.split("/");
@@ -40,11 +36,11 @@
        String sessionId = val.getId();
        String workspaceKey = RedisConst.WEBSOCKET_PREFIX + name[0];
        String userTypeKey = RedisConst.WEBSOCKET_PREFIX + UserTypeEnum.find(Integer.parseInt(name[1])).getDesc();
        redisOps.hashSet(workspaceKey, sessionId, name[2]);
        redisOps.hashSet(userTypeKey, sessionId, name[2]);
        RedisOpsUtils.hashSet(workspaceKey, sessionId, name[2]);
        RedisOpsUtils.hashSet(userTypeKey, sessionId, name[2]);
        SESSIONS.put(sessionId, val);
        redisOps.expireKey(workspaceKey, RedisConst.WEBSOCKET_ALIVE_SECOND);
        redisOps.expireKey(userTypeKey, RedisConst.WEBSOCKET_ALIVE_SECOND);
        RedisOpsUtils.expireKey(workspaceKey, RedisConst.WEBSOCKET_ALIVE_SECOND);
        RedisOpsUtils.expireKey(userTypeKey, RedisConst.WEBSOCKET_ALIVE_SECOND);
    }
    @Override
@@ -54,8 +50,8 @@
            log.debug("The key is out of format. [{workspaceId}/{userType}/{userId}]");
            return;
        }
        redisOps.hashDel(RedisConst.WEBSOCKET_PREFIX + name[0], new String[] {sessionId});
        redisOps.hashDel(RedisConst.WEBSOCKET_PREFIX + UserTypeEnum.find(Integer.parseInt(name[1])).getDesc(), new String[] {sessionId});
        RedisOpsUtils.hashDel(RedisConst.WEBSOCKET_PREFIX + name[0], new String[] {sessionId});
        RedisOpsUtils.hashDel(RedisConst.WEBSOCKET_PREFIX + UserTypeEnum.find(Integer.parseInt(name[1])).getDesc(), new String[] {sessionId});
        SESSIONS.remove(sessionId);
    }
@@ -66,7 +62,7 @@
        }
        String key = RedisConst.WEBSOCKET_PREFIX + workspaceId;
        return redisOps.hashKeys(key)
        return RedisOpsUtils.hashKeys(key)
                .stream()
                .map(SESSIONS::get)
                .filter(Objects::nonNull)
@@ -76,7 +72,7 @@
    @Override
    public Collection<ConcurrentWebSocketSession> getValueWithWorkspaceAndUserType(String workspaceId, Integer userType) {
        String key = RedisConst.WEBSOCKET_PREFIX + UserTypeEnum.find(userType).getDesc();
        return redisOps.hashKeys(key)
        return RedisOpsUtils.hashKeys(key)
                .stream()
                .map(SESSIONS::get)
                .filter(Objects::nonNull)
src/main/java/com/dji/sample/control/model/dto/AlarmState.java
New file
@@ -0,0 +1,29 @@
package com.dji.sample.control.model.dto;
import com.dji.sample.manage.model.enums.StateSwitchEnum;
import com.dji.sample.manage.model.receiver.BasicDeviceProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.Objects;
/**
 * @author sean
 * @version 1.3
 * @date 2022/11/25
 */
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AlarmState extends BasicDeviceProperty {
    private Integer action;
    @Override
    public boolean valid() {
        return Objects.nonNull(action) && StateSwitchEnum.find(action).isPresent();
    }
}
src/main/java/com/dji/sample/control/model/dto/BatteryStoreMode.java
@@ -20,10 +20,10 @@
@NoArgsConstructor
public class BatteryStoreMode extends BasicDeviceProperty {
    private Integer value;
    private Integer action;
    @Override
    public boolean valid() {
        return Objects.nonNull(value) && BatteryStoreModeEnum.find(value).isPresent();
        return Objects.nonNull(action) && BatteryStoreModeEnum.find(action).isPresent();
    }
}
src/main/java/com/dji/sample/control/model/dto/LinkWorkMode.java
New file
@@ -0,0 +1,31 @@
package com.dji.sample.control.model.dto;
import com.dji.sample.control.model.enums.LinkWorkModeEnum;
import com.dji.sample.manage.model.receiver.BasicDeviceProperty;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.Objects;
/**
 * @author sean
 * @version 1.3
 * @date 2022/11/25
 */
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LinkWorkMode extends BasicDeviceProperty {
    @JsonProperty("link_workmode")
    private Integer linkWorkMode;
    @Override
    public boolean valid() {
        return Objects.nonNull(linkWorkMode) && LinkWorkModeEnum.find(linkWorkMode).isPresent();
    }
}
src/main/java/com/dji/sample/control/model/enums/LinkWorkModeEnum.java
New file
@@ -0,0 +1,29 @@
package com.dji.sample.control.model.enums;
import lombok.Getter;
import java.util.Arrays;
import java.util.Optional;
/**
 * @author sean
 * @version 1.3
 * @date 2022/11/25
 */
@Getter
public enum LinkWorkModeEnum {
    SDR_ONLY(0),
    SDR_WITH_4G(1);
    int mode;
    LinkWorkModeEnum(Integer mode) {
        this.mode = mode;
    }
    public static Optional<LinkWorkModeEnum> find(int mode) {
        return Arrays.stream(LinkWorkModeEnum.values()).filter(modeEnum -> modeEnum.mode == mode).findAny();
    }
}
src/main/java/com/dji/sample/control/model/enums/RemoteControlMethodEnum.java
@@ -1,7 +1,8 @@
package com.dji.sample.control.model.enums;
import com.dji.sample.control.model.dto.AlarmState;
import com.dji.sample.control.model.dto.BatteryStoreMode;
import com.dji.sample.manage.model.enums.StateSwitchReceiver;
import com.dji.sample.control.model.dto.LinkWorkMode;
import com.dji.sample.manage.model.receiver.BasicDeviceProperty;
import lombok.Getter;
@@ -24,8 +25,6 @@
    SUPPLEMENT_LIGHT_CLOSE("supplement_light_close", false, null),
    RETURN_HOME("return_home", false, null),
    SDR_WORKMODE_SWITCH("sdr_workmode_switch", false, null),
    DEVICE_REBOOT("device_reboot", true, null),
@@ -51,11 +50,13 @@
    CHARGE_CLOSE("charge_close", true, null),
    BATTERY_MAINTENANCE_SWITCH("battery_maintenance_switch", true, StateSwitchReceiver.class),
    BATTERY_MAINTENANCE_SWITCH("battery_maintenance_switch", true, AlarmState.class),
    
    ALARM_STATE_SWITCH("alarm_state_switch", true, StateSwitchReceiver.class),
    ALARM_STATE_SWITCH("alarm_state_switch", true, AlarmState.class),
    
    BATTERY_STORE_MODE_SWITCH("battery_store_mode_switch", true, BatteryStoreMode.class),
    SDR_WORK_MODE_SWITCH("sdr_workmode_switch", false, LinkWorkMode.class),
    
    UNKNOWN("unknown", false, null);
src/main/java/com/dji/sample/control/service/impl/ControlServiceImpl.java
@@ -38,9 +38,6 @@
public class ControlServiceImpl implements IControlService {
    @Autowired
    private RedisOpsUtils redisOps;
    @Autowired
    private IMessageSenderService messageSenderService;
    @Autowired
@@ -62,6 +59,7 @@
            return ResponseResult.error("The " + serviceIdentifier + " method does not exist.");
        }
        Object data = "";
        // Add parameter validation.
        if (Objects.nonNull(controlMethodEnum.getClazz())) {
            if (Objects.isNull(param)) {
@@ -71,6 +69,7 @@
            if (!basicDeviceProperty.valid()) {
                return ResponseResult.error(CommonErrorEnum.ILLEGAL_ARGUMENT);
            }
            data = basicDeviceProperty;
        }
        boolean isExist = deviceService.checkDeviceOnline(sn);
@@ -85,16 +84,17 @@
                        .bid(bid)
                        .method(serviceIdentifier)
                        .timestamp(System.currentTimeMillis())
                        .data(Objects.requireNonNullElse(param, ""))
                        .data(data)
                        .build());
        ServiceReply<EventsOutputReceiver> serviceReply = mapper.convertValue(
                serviceReplyOpt, new TypeReference<ServiceReply<EventsOutputReceiver>>() {});
        if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) {
            return ResponseResult.error(serviceReply.getResult(), serviceReply.getOutput().getStatus());
            return ResponseResult.error(serviceReply.getResult(),
                    Objects.nonNull(serviceReply.getOutput()) ? serviceReply.getOutput().getStatus() : "error: " + serviceIdentifier);
        }
        if (controlMethodEnum.getProgress()) {
            redisOps.setWithExpire(serviceIdentifier + RedisConst.DELIMITER +  bid, sn,
            RedisOpsUtils.setWithExpire(serviceIdentifier + RedisConst.DELIMITER +  bid, sn,
                    RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND);
        }
        return ResponseResult.success();
@@ -104,10 +104,10 @@
    @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, outputChannel = ChannelName.OUTBOUND)
    public void handleControlProgress(CommonTopicReceiver receiver, MessageHeaders headers) {
        String key = receiver.getMethod() + RedisConst.DELIMITER + receiver.getBid();
        if (redisOps.getExpire(key) <= 0) {
        if (RedisOpsUtils.getExpire(key) <= 0) {
            return;
        }
        String sn = redisOps.get(key).toString();
        String sn = RedisOpsUtils.get(key).toString();
        EventsReceiver<EventsOutputReceiver> eventsReceiver = mapper.convertValue(receiver.getData(),
                new TypeReference<EventsReceiver<EventsOutputReceiver>>(){});
@@ -123,10 +123,10 @@
        if (eventsReceiver.getOutput().getProgress().getPercent() == 100 ||
                EventsResultStatusEnum.find(eventsReceiver.getOutput().getStatus()).getEnd()) {
            redisOps.del(key);
            RedisOpsUtils.del(key);
        }
        DeviceDTO device = (DeviceDTO) redisOps.get(RedisConst.DEVICE_ONLINE_PREFIX + sn);
        DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + sn);
        webSocketMessageService.sendBatch(
                webSocketManageService.getValueWithWorkspaceAndUserType(
                        device.getWorkspaceId(), UserTypeEnum.WEB.getVal()),
src/main/java/com/dji/sample/manage/controller/DeviceFirmwareController.java
@@ -1,17 +1,28 @@
package com.dji.sample.manage.controller;
import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.common.model.PaginationData;
import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.manage.model.dto.DeviceFirmwareDTO;
import com.dji.sample.manage.model.dto.DeviceFirmwareNoteDTO;
import com.dji.sample.manage.model.dto.FirmwareFileProperties;
import com.dji.sample.manage.model.param.DeviceFirmwareQueryParam;
import com.dji.sample.manage.model.param.DeviceFirmwareUpdateParam;
import com.dji.sample.manage.model.param.DeviceFirmwareUploadParam;
import com.dji.sample.manage.service.IDeviceFirmwareService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;
/**
 * @author sean
@@ -20,6 +31,7 @@
 */
@RestController
@RequestMapping("${url.manage.prefix}${url.manage.version}/workspaces")
@Validated
public class DeviceFirmwareController {
    @Autowired
@@ -40,4 +52,60 @@
        return ResponseResult.success(releaseNotes);
    }
    /**
     * Query firmware information based on parameters.
     * @param workspaceId
     * @param param
     * @return
     */
    @GetMapping("/{workspace_id}/firmwares")
    public ResponseResult<PaginationData<DeviceFirmwareDTO>> getAllFirmwarePagination(
            @PathVariable("workspace_id") String workspaceId, @Valid DeviceFirmwareQueryParam param) {
        PaginationData<DeviceFirmwareDTO> data = service.getAllFirmwarePagination(workspaceId, param);
        return ResponseResult.success(data);
    }
    /**
     * Import firmware file for device upgrades.
     * @param request
     * @param workspaceId
     * @param file
     * @param param
     * @return
     */
    @PostMapping("/{workspace_id}/firmwares/file/upload")
    public ResponseResult importFirmwareFile(HttpServletRequest request, @PathVariable("workspace_id") String workspaceId,
                                             @NotNull(message = "No file received.") MultipartFile file,
                                             @Valid DeviceFirmwareUploadParam param) {
        if (!file.getOriginalFilename().endsWith(FirmwareFileProperties.FIRMWARE_FILE_SUFFIX)) {
            return ResponseResult.error("The file format is incorrect.");
        }
        CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
        String creator = customClaim.getUsername();
        service.importFirmwareFile(workspaceId, creator, param, file);
        return ResponseResult.success();
    }
    /**
     * Change the firmware availability status.
     * @param workspaceId
     * @param firmwareId
     * @param param
     * @return
     */
    @PutMapping("/{workspace_id}/firmwares/{firmware_id}")
    public ResponseResult importFirmwareFile(@PathVariable("workspace_id") String workspaceId,
                                             @PathVariable("firmware_id") String firmwareId,
                                             @Valid @RequestBody DeviceFirmwareUpdateParam param) {
        service.updateFirmwareInfo(DeviceFirmwareDTO.builder()
                .firmwareId(firmwareId).firmwareStatus(param.getStatus()).build());
        return ResponseResult.success();
    }
}
src/main/java/com/dji/sample/manage/model/dto/DeviceFirmwareDTO.java
@@ -24,7 +24,7 @@
    private String productVersion;
    private String fileUrl;
    private String objectKey;
    private Long fileSize;
@@ -37,4 +37,8 @@
    private LocalDate releasedTime;
    private Boolean firmwareStatus;
    private String workspaceId;
    private String username;
}
src/main/java/com/dji/sample/manage/model/dto/FirmwareFileProperties.java
New file
@@ -0,0 +1,28 @@
package com.dji.sample.manage.model.dto;
/**
 * @author sean
 * @version 1.3
 * @date 2022/12/1
 */
public class FirmwareFileProperties {
    private FirmwareFileProperties() {
    }
    public static final String FIRMWARE_FILE_SUFFIX = ".zip";
    public static final String FIRMWARE_SIG_FILE_SUFFIX = ".sig";
    public static final String FIRMWARE_CONFIG_FILE_SUFFIX = ".cfg";
    public static final String FIRMWARE_FILE_DELIMITER = "_";
    public static final int FILENAME_VERSION_INDEX = 2;
    public static final int FILENAME_RELEASE_DATE_INDEX = 3;
    public static final String FILENAME_RELEASE_DATE_FORMAT = "yyyyMMdd";
}
src/main/java/com/dji/sample/manage/model/entity/DeviceFirmwareEntity.java
@@ -34,8 +34,8 @@
    @TableField("firmware_version")
    private String firmwareVersion;
    @TableField("file_url")
    private String fileUrl;
    @TableField("object_key")
    private String objectKey;
    @TableField("file_size")
    private Long fileSize;
@@ -55,6 +55,12 @@
    @TableField("status")
    private Boolean status;
    @TableField("workspace_id")
    private String workspaceId;
    @TableField("user_name")
    private String username;
    @TableField(value = "create_time", fill = FieldFill.INSERT)
    private Long createTime;
src/main/java/com/dji/sample/manage/model/enums/DeviceSetPropertyEnum.java
@@ -1,9 +1,6 @@
package com.dji.sample.manage.model.enums;
import com.dji.sample.manage.model.receiver.BasicDeviceProperty;
import com.dji.sample.manage.model.receiver.DistanceLimitStatusReceiver;
import com.dji.sample.manage.model.receiver.HeightLimitReceiver;
import com.dji.sample.manage.model.receiver.ObstacleAvoidanceReceiver;
import com.dji.sample.manage.model.receiver.*;
import lombok.Getter;
import java.util.Arrays;
@@ -17,7 +14,7 @@
@Getter
public enum DeviceSetPropertyEnum {
    NIGHT_LIGHTS_STATE("night_lights_state", StateSwitchReceiver.class),
    NIGHT_LIGHTS_STATE("night_lights_state", NightLightsStateReceiver.class),
    HEIGHT_LIMIT("height_limit", HeightLimitReceiver.class),
src/main/java/com/dji/sample/manage/model/enums/StateSwitchEnum.java
New file
@@ -0,0 +1,18 @@
package com.dji.sample.manage.model.enums;
import java.util.Arrays;
import java.util.Optional;
/**
 * @author sean
 * @version 1.3
 * @date 2022/10/28
 */
public enum StateSwitchEnum {
    DISABLE, ENABLE;
    public static Optional<StateSwitchEnum> find(int value) {
        return Arrays.stream(StateSwitchEnum.values()).filter(state -> state.ordinal() == value).findAny();
    }
}
src/main/java/com/dji/sample/manage/model/enums/StateSwitchReceiver.java
File was deleted
src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareQueryParam.java
New file
@@ -0,0 +1,32 @@
package com.dji.sample.manage.model.param;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
/**
 * @author sean
 * @version 1.3
 * @date 2022/12/1
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class DeviceFirmwareQueryParam {
    private String deviceName;
    private String productVersion;
    private Boolean status;
    @NotNull
    private Long page;
    @NotNull
    private Long pageSize;
}
src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareUpdateParam.java
New file
@@ -0,0 +1,17 @@
package com.dji.sample.manage.model.param;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
 * @author sean
 * @version 1.3
 * @date 2022/12/6
 */
@Data
public class DeviceFirmwareUpdateParam {
    @NotNull
    private Boolean status;
}
src/main/java/com/dji/sample/manage/model/param/DeviceFirmwareUploadParam.java
New file
@@ -0,0 +1,23 @@
package com.dji.sample.manage.model.param;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
 * @author sean
 * @version 1.3
 * @date 2022/12/1
 */
@Data
public class DeviceFirmwareUploadParam {
    @NotNull
    private String releaseNote;
    @NotNull
    private Boolean status;
    @NotNull
    private String deviceName;
}
src/main/java/com/dji/sample/manage/model/receiver/DistanceLimitStatusReceiver.java
@@ -1,8 +1,9 @@
package com.dji.sample.manage.model.receiver;
import com.dji.sample.manage.model.enums.StateSwitchReceiver;
import com.dji.sample.manage.model.enums.StateSwitchEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.Objects;
@@ -13,6 +14,7 @@
 * @version 1.3
 * @date 2022/10/27
 */
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
@@ -30,10 +32,10 @@
    public boolean valid() {
        boolean valid = Objects.nonNull(state) || Objects.nonNull(distanceLimit);
        if (Objects.nonNull(state)) {
            valid = new StateSwitchReceiver(this.state).valid();
            valid = StateSwitchEnum.find(state).isPresent();
        }
        if (Objects.nonNull(distanceLimit)) {
            valid &= distanceLimit >= DISTANCE_MIN && distanceLimit <= DISTANCE_MAX;
            valid &= StateSwitchEnum.find(distanceLimit).isPresent();
        }
        return valid;
    }
src/main/java/com/dji/sample/manage/model/receiver/HeightLimitReceiver.java
@@ -2,6 +2,7 @@
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.Objects;
@@ -11,6 +12,7 @@
 * @version 1.3
 * @date 2022/10/28
 */
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
src/main/java/com/dji/sample/manage/model/receiver/NightLightsStateReceiver.java
New file
@@ -0,0 +1,28 @@
package com.dji.sample.manage.model.receiver;
import com.dji.sample.manage.model.enums.StateSwitchEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.Objects;
/**
 * @author sean
 * @version 1.3
 * @date 2022/11/25
 */
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class NightLightsStateReceiver extends BasicDeviceProperty {
    private Integer value;
    @Override
    public boolean valid() {
        return Objects.nonNull(value) && StateSwitchEnum.find(value).isPresent();
    }
}
src/main/java/com/dji/sample/manage/model/receiver/ObstacleAvoidanceReceiver.java
@@ -1,7 +1,8 @@
package com.dji.sample.manage.model.receiver;
import com.dji.sample.manage.model.enums.StateSwitchReceiver;
import com.dji.sample.manage.model.enums.StateSwitchEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Objects;
@@ -10,6 +11,7 @@
 * @version 1.3
 * @date 2022/10/27
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class ObstacleAvoidanceReceiver extends BasicDeviceProperty {
@@ -23,18 +25,14 @@
    public boolean valid() {
        boolean valid = Objects.nonNull(this.horizon) || Objects.nonNull(this.upside) || Objects.nonNull(this.downside);
        StateSwitchReceiver stateSwitch = new StateSwitchReceiver();
        if (Objects.nonNull(this.horizon)) {
            stateSwitch.setValue(this.horizon);
            valid = stateSwitch.valid();
            valid = StateSwitchEnum.find(horizon).isPresent();
        }
        if (Objects.nonNull(this.upside)) {
            stateSwitch.setValue(this.upside);
            valid &= stateSwitch.valid();
            valid &= StateSwitchEnum.find(upside).isPresent();
        }
        if (Objects.nonNull(this.downside)) {
            stateSwitch.setValue(this.downside);
            valid &= stateSwitch.valid();
            valid &= StateSwitchEnum.find(downside).isPresent();
        }
        return valid;
    }
src/main/java/com/dji/sample/manage/service/IDeviceFirmwareService.java
@@ -1,11 +1,15 @@
package com.dji.sample.manage.service;
import com.dji.sample.common.model.PaginationData;
import com.dji.sample.component.mqtt.model.CommonTopicReceiver;
import com.dji.sample.manage.model.dto.DeviceFirmwareDTO;
import com.dji.sample.manage.model.dto.DeviceFirmwareNoteDTO;
import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO;
import com.dji.sample.manage.model.param.DeviceFirmwareQueryParam;
import com.dji.sample.manage.model.param.DeviceFirmwareUploadParam;
import com.dji.sample.manage.model.param.DeviceOtaCreateParam;
import org.springframework.messaging.MessageHeaders;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Optional;
@@ -45,4 +49,43 @@
     * @param headers
     */
    void handleOtaProgress(CommonTopicReceiver receiver, MessageHeaders headers);
    /**
     * Query firmware version information by page.
     *
     * @param workspaceId
     * @param param
     * @return
     */
    PaginationData<DeviceFirmwareDTO> getAllFirmwarePagination(String workspaceId, DeviceFirmwareQueryParam param);
    /**
     * Checks for file existence based on md5.
     *
     * @param workspaceId
     * @param fileMd5
     * @return
     */
    Boolean checkFileExist(String workspaceId, String fileMd5);
    /**
     * Import firmware file for device upgrades.
     * @param workspaceId
     * @param creator
     * @param param
     * @param file
     */
    void importFirmwareFile(String workspaceId, String creator, DeviceFirmwareUploadParam param, MultipartFile file);
    /**
     * Save the file information of the firmware.
     * @param firmware
     */
    void saveFirmwareInfo(DeviceFirmwareDTO firmware);
    /**
     * Update the file information of the firmware.
     * @param firmware
     */
    void updateFirmwareInfo(DeviceFirmwareDTO firmware);
}
src/main/java/com/dji/sample/manage/service/impl/CapacityCameraServiceImpl.java
@@ -34,25 +34,22 @@
    @Autowired
    private IDeviceDictionaryService dictionaryService;
    @Autowired
    private RedisOpsUtils redisOps;
    @Override
    public List<CapacityCameraDTO> getCapacityCameraByDeviceSn(String deviceSn) {
        return (List<CapacityCameraDTO>) redisOps.hashGet(StateDataEnum.LIVE_CAPACITY.getDesc(), deviceSn);
        return (List<CapacityCameraDTO>) RedisOpsUtils.hashGet(StateDataEnum.LIVE_CAPACITY.getDesc(), deviceSn);
    }
    @Override
    public Boolean deleteCapacityCameraByDeviceSn(String deviceSn) {
        return redisOps.hashDel(StateDataEnum.LIVE_CAPACITY.getDesc(), new String[]{deviceSn});
        return RedisOpsUtils.hashDel(StateDataEnum.LIVE_CAPACITY.getDesc(), new String[]{deviceSn});
    }
    @Override
    public void saveCapacityCameraReceiverList(List<CapacityCameraReceiver> capacityCameraReceivers, String deviceSn, Long timestamp) {
        List<CapacityCameraDTO> capacity = capacityCameraReceivers.stream()
                .map(this::receiver2Dto).collect(Collectors.toList());
        redisOps.hashSet(StateDataEnum.LIVE_CAPACITY.getDesc(), deviceSn, capacity);
        redisOps.setWithExpire(StateDataEnum.LIVE_CAPACITY + RedisConst.DELIMITER + deviceSn, timestamp, RedisConst.DEVICE_ALIVE_SECOND);
        RedisOpsUtils.hashSet(StateDataEnum.LIVE_CAPACITY.getDesc(), deviceSn, capacity);
        RedisOpsUtils.setWithExpire(StateDataEnum.LIVE_CAPACITY + RedisConst.DELIMITER + deviceSn, timestamp, RedisConst.DEVICE_ALIVE_SECOND);
    }
    @Override
src/main/java/com/dji/sample/manage/service/impl/DeviceFirmwareServiceImpl.java
@@ -1,21 +1,26 @@
package com.dji.sample.manage.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dji.sample.common.model.Pagination;
import com.dji.sample.common.model.PaginationData;
import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.component.mqtt.model.*;
import com.dji.sample.component.mqtt.service.impl.MessageSenderServiceImpl;
import com.dji.sample.component.oss.model.OssConfiguration;
import com.dji.sample.component.oss.service.impl.OssServiceContext;
import com.dji.sample.component.redis.RedisConst;
import com.dji.sample.component.redis.RedisOpsUtils;
import com.dji.sample.component.websocket.model.CustomWebSocketMessage;
import com.dji.sample.component.websocket.service.IWebSocketManageService;
import com.dji.sample.component.websocket.service.impl.SendMessageServiceImpl;
import com.dji.sample.manage.dao.IDeviceFirmwareMapper;
import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.dto.DeviceFirmwareDTO;
import com.dji.sample.manage.model.dto.DeviceFirmwareNoteDTO;
import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO;
import com.dji.sample.manage.model.dto.*;
import com.dji.sample.manage.model.entity.DeviceFirmwareEntity;
import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.dji.sample.manage.model.param.DeviceFirmwareQueryParam;
import com.dji.sample.manage.model.param.DeviceFirmwareUploadParam;
import com.dji.sample.manage.model.param.DeviceOtaCreateParam;
import com.dji.sample.manage.service.IDeviceFirmwareService;
import com.dji.sample.manage.service.IDeviceService;
@@ -27,13 +32,22 @@
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
 * @author sean
@@ -46,9 +60,6 @@
    @Autowired
    private IDeviceFirmwareMapper mapper;
    @Autowired
    private RedisOpsUtils redisOps;
    @Autowired
    private MessageSenderServiceImpl messageSenderService;
@@ -65,6 +76,9 @@
    @Autowired
    private IDeviceService deviceService;
    @Autowired
    private OssServiceContext ossServiceContext;
    @Override
    public Optional<DeviceFirmwareDTO> getFirmware(String deviceName, String version) {
        return Optional.ofNullable(entity2Dto(mapper.selectOne(
@@ -79,7 +93,7 @@
                new LambdaQueryWrapper<DeviceFirmwareEntity>()
                        .eq(DeviceFirmwareEntity::getDeviceName, deviceName)
                        .eq(DeviceFirmwareEntity::getStatus, true)
                        .orderByDesc(DeviceFirmwareEntity::getReleaseDate)
                        .orderByDesc(DeviceFirmwareEntity::getReleaseDate, DeviceFirmwareEntity::getFirmwareVersion)
                        .last(" limit 1 "))));
    }
@@ -127,30 +141,30 @@
            log.error("SN: {}, {} ===> Error code: {}", sn, receiver.getMethod(), eventsReceiver.getResult());
        }
        DeviceDTO device = (DeviceDTO) redisOps.get(RedisConst.DEVICE_ONLINE_PREFIX + sn);
        DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + sn);
        String childDeviceSn = device.getChildDeviceSn();
        boolean upgrade = redisOps.getExpire(RedisConst.FIRMWARE_UPGRADING_PREFIX + sn) > 0;
        boolean childUpgrade = redisOps.getExpire(RedisConst.FIRMWARE_UPGRADING_PREFIX + childDeviceSn) > 0;
        boolean upgrade = RedisOpsUtils.getExpire(RedisConst.FIRMWARE_UPGRADING_PREFIX + sn) > 0;
        boolean childUpgrade = RedisOpsUtils.getExpire(RedisConst.FIRMWARE_UPGRADING_PREFIX + childDeviceSn) > 0;
        // Determine whether it is the ending state, delete the update state key in redis after the job ends.
        EventsResultStatusEnum statusEnum = EventsResultStatusEnum.find(output.getStatus());
        if (upgrade) {
            if (statusEnum.getEnd()) {
                // Delete the cache after the update is complete.
                redisOps.del(RedisConst.FIRMWARE_UPGRADING_PREFIX + sn);
                RedisOpsUtils.del(RedisConst.FIRMWARE_UPGRADING_PREFIX + sn);
            } else {
                // Update the update progress of the dock in redis.
                redisOps.setWithExpire(
                RedisOpsUtils.setWithExpire(
                        RedisConst.FIRMWARE_UPGRADING_PREFIX + sn, output.getProgress().getPercent(),
                        RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND);
            }
        }
        if (childUpgrade) {
            if (statusEnum.getEnd()) {
                redisOps.del(RedisConst.FIRMWARE_UPGRADING_PREFIX + childDeviceSn);
                RedisOpsUtils.del(RedisConst.FIRMWARE_UPGRADING_PREFIX + childDeviceSn);
            } else {
                // Update the update progress of the drone in redis.
                redisOps.setWithExpire(
                RedisOpsUtils.setWithExpire(
                        RedisConst.FIRMWARE_UPGRADING_PREFIX + childDeviceSn, output.getProgress().getPercent(),
                        RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND);
            }
@@ -178,6 +192,137 @@
        }
    }
    @Override
    public Boolean checkFileExist(String workspaceId, String fileMd5) {
        return mapper.selectCount(new LambdaQueryWrapper<DeviceFirmwareEntity>()
                    .eq(DeviceFirmwareEntity::getWorkspaceId, workspaceId)
                    .eq(DeviceFirmwareEntity::getFileMd5, fileMd5))
                > 0;
    }
    @Override
    public PaginationData<DeviceFirmwareDTO> getAllFirmwarePagination(String workspaceId, DeviceFirmwareQueryParam param) {
        Page<DeviceFirmwareEntity> page = mapper.selectPage(new Page<>(param.getPage(), param.getPageSize()),
                new LambdaQueryWrapper<DeviceFirmwareEntity>()
                        .eq(DeviceFirmwareEntity::getWorkspaceId, workspaceId)
                        .eq(Objects.nonNull(param.getStatus()), DeviceFirmwareEntity::getStatus, param.getStatus())
                        .eq(StringUtils.hasText(param.getDeviceName()), DeviceFirmwareEntity::getDeviceName, param.getDeviceName())
                        .like(StringUtils.hasText(param.getProductVersion()), DeviceFirmwareEntity::getFirmwareVersion, param.getProductVersion())
                        .orderByDesc(DeviceFirmwareEntity::getReleaseDate));
        List<DeviceFirmwareDTO> data = page.getRecords().stream().map(this::entity2Dto).collect(Collectors.toList());
        return new PaginationData<DeviceFirmwareDTO>(data, new Pagination(page));
    }
    @Override
    public void importFirmwareFile(String workspaceId, String creator, DeviceFirmwareUploadParam param, MultipartFile file) {
        try (InputStream is = file.getInputStream()) {
            long size = is.available();
            String md5 = DigestUtils.md5DigestAsHex(is);
            boolean exist = checkFileExist(workspaceId, md5);
            if (exist) {
                throw new RuntimeException("The file already exists.");
            }
            Optional<DeviceFirmwareDTO> firmwareOpt = verifyFirmwareFile(file);
            if (firmwareOpt.isEmpty()) {
                throw new RuntimeException("The file format is incorrect.");
            }
            String firmwareId = UUID.randomUUID().toString();
            String objectKey = OssConfiguration.objectDirPrefix + File.separator + firmwareId + FirmwareFileProperties.FIRMWARE_FILE_SUFFIX;
            ossServiceContext.putObject(OssConfiguration.bucket, objectKey, file.getInputStream());
            log.info("upload success");
            DeviceFirmwareDTO firmware = DeviceFirmwareDTO.builder()
                    .deviceName(param.getDeviceName())
                    .releaseNote(param.getReleaseNote())
                    .firmwareStatus(param.getStatus())
                    .fileMd5(md5)
                    .objectKey(objectKey)
                    .fileName(file.getOriginalFilename())
                    .workspaceId(workspaceId)
                    .username(creator)
                    .fileSize(size)
                    .productVersion(firmwareOpt.get().getProductVersion())
                    .releasedTime(firmwareOpt.get().getReleasedTime())
                    .firmwareId(firmwareId)
                    .build();
            saveFirmwareInfo(firmware);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void saveFirmwareInfo(DeviceFirmwareDTO firmware) {
         mapper.insert(dto2Entity(firmware));
    }
    @Override
    public void updateFirmwareInfo(DeviceFirmwareDTO firmware) {
        mapper.update(dto2Entity(firmware),
                new LambdaUpdateWrapper<DeviceFirmwareEntity>()
                        .eq(DeviceFirmwareEntity::getFirmwareId, firmware.getFirmwareId()));
    }
    /**
     * Parse firmware file information.
     * @param file
     * @return
     */
    private Optional<DeviceFirmwareDTO> verifyFirmwareFile(MultipartFile file) {
        try (ZipInputStream unzipFile = new ZipInputStream(file.getInputStream(), StandardCharsets.UTF_8)) {
            ZipEntry nextEntry = unzipFile.getNextEntry();
            while (Objects.nonNull(nextEntry)) {
                String configName = nextEntry.getName();
                if (!configName.contains(File.separator) && configName.endsWith(FirmwareFileProperties.FIRMWARE_CONFIG_FILE_SUFFIX + FirmwareFileProperties.FIRMWARE_SIG_FILE_SUFFIX)) {
                    String[] filenameArr = configName.split(FirmwareFileProperties.FIRMWARE_FILE_DELIMITER);
                    String date = filenameArr[FirmwareFileProperties.FILENAME_RELEASE_DATE_INDEX];
                    int index = date.indexOf(".");
                    if (index != -1) {
                        date = date.substring(0, index);
                    }
                    return Optional.of(DeviceFirmwareDTO.builder()
                            .releasedTime(LocalDate.parse(
                                    date,
                                    DateTimeFormatter.ofPattern(FirmwareFileProperties.FILENAME_RELEASE_DATE_FORMAT)))
                            // delete the string v.
                            .productVersion(filenameArr[FirmwareFileProperties.FILENAME_VERSION_INDEX].substring(1))
                            .build());
                }
                nextEntry = unzipFile.getNextEntry();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Optional.empty();
    }
    private DeviceFirmwareEntity dto2Entity(DeviceFirmwareDTO dto) {
        if (dto == null) {
            return null;
        }
        return DeviceFirmwareEntity.builder()
                .fileName(dto.getFileName())
                .deviceName(dto.getDeviceName())
                .fileMd5(dto.getFileMd5())
                .fileSize(dto.getFileSize())
                .firmwareId(dto.getFirmwareId())
                .firmwareVersion(dto.getProductVersion())
                .objectKey(dto.getObjectKey())
                .releaseDate(Objects.nonNull(dto.getReleasedTime()) ?
                        dto.getReleasedTime().atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli() : null)
                .releaseNote(dto.getReleaseNote())
                .status(dto.getFirmwareStatus())
                .workspaceId(dto.getWorkspaceId())
                .username(dto.getUsername())
                .build();
    }
    private DeviceFirmwareNoteDTO entity2NoteDto (DeviceFirmwareEntity entity) {
        if (entity == null) {
            return null;
@@ -198,12 +343,15 @@
                .deviceName(entity.getDeviceName())
                .fileMd5(entity.getFileMd5())
                .fileSize(entity.getFileSize())
                .fileUrl(entity.getFileUrl())
                .objectKey(entity.getObjectKey())
                .firmwareId(entity.getFirmwareId())
                .fileName(entity.getFileName())
                .productVersion(entity.getFirmwareVersion())
                .releasedTime(LocalDate.ofInstant(Instant.ofEpochMilli(entity.getReleaseDate()), ZoneId.systemDefault()))
                .releaseNote(entity.getReleaseNote())
                .firmwareStatus(entity.getStatus())
                .workspaceId(entity.getWorkspaceId())
                .username(entity.getUsername())
                .build();
    }
@@ -213,7 +361,7 @@
        }
        return DeviceOtaCreateParam.builder()
                .fileSize(dto.getFileSize())
                .fileUrl(dto.getFileUrl())
                .fileUrl(ossServiceContext.getObjectUrl(OssConfiguration.bucket, dto.getObjectKey()).toString())
                .fileName(dto.getFileName())
                .md5(dto.getFileMd5())
                .productVersion(dto.getProductVersion())
src/main/java/com/dji/sample/manage/service/impl/DeviceHmsServiceImpl.java
@@ -63,9 +63,6 @@
    private ObjectMapper objectMapper;
    @Autowired
    private RedisOpsUtils redisOps;
    @Autowired
    private SendMessageServiceImpl sendMessageService;
    @Autowired
@@ -95,9 +92,9 @@
                .build();
        String key = RedisConst.HMS_PREFIX + sn;
        // Query all unread hms messages of the device in redis.
        Set<String> hmsMap = redisOps.listGetAll(key).stream().map(String::valueOf).collect(Collectors.toSet());
        Set<String> hmsMap = RedisOpsUtils.listGetAll(key).stream().map(String::valueOf).collect(Collectors.toSet());
        DeviceDTO device = (DeviceDTO) redisOps.get(RedisConst.DEVICE_ONLINE_PREFIX + sn);
        DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + sn);
        List<DeviceHmsDTO> unReadList = new ArrayList<>();
        objectMapper.convertValue(((Map) (receiver.getData())).get(MapKeyConst.LIST),
@@ -117,7 +114,7 @@
        if (unReadList.isEmpty()) {
            return;
        }
        redisOps.listRPush(key, unReadList.stream().map(DeviceHmsDTO::getKey).toArray(String[]::new));
        RedisOpsUtils.listRPush(key, unReadList.stream().map(DeviceHmsDTO::getKey).toArray(String[]::new));
        // push to the web
        Collection<ConcurrentWebSocketSession> sessions = webSocketManageService.getValueWithWorkspaceAndUserType(
                device.getWorkspaceId(), UserTypeEnum.WEB.getVal());
@@ -162,7 +159,7 @@
                        .eq(DeviceHmsEntity::getSn, deviceSn)
                        .eq(DeviceHmsEntity::getUpdateTime, 0L));
        // Delete unread messages cached in redis.
        redisOps.del(RedisConst.HMS_PREFIX + deviceSn);
        RedisOpsUtils.del(RedisConst.HMS_PREFIX + deviceSn);
    }
    private DeviceHmsDTO entity2Dto(DeviceHmsEntity entity) {
src/main/java/com/dji/sample/manage/service/impl/DevicePayloadServiceImpl.java
@@ -43,9 +43,6 @@
    @Autowired
    private ICapacityCameraService capacityCameraService;
    @Autowired
    private RedisOpsUtils redisOps;
    @Override
    public Integer checkPayloadExist(String payloadSn) {
        DevicePayloadEntity devicePayload = mapper.selectOne(
@@ -72,7 +69,7 @@
        String deviceSn = payloadReceiverList.get(0).getDeviceSn();
        String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn;
        DeviceDTO device = (DeviceDTO) redisOps.get(key);
        DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(key);
        List<DevicePayloadDTO> payloads = new ArrayList<>();
        for (DevicePayloadReceiver payloadReceiver : payloadReceiverList) {
@@ -87,7 +84,7 @@
            payloads = this.getDevicePayloadEntitiesByDeviceSn(deviceSn);
        }
        device.setPayloadsList(payloads);
        redisOps.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn(), device, RedisConst.DEVICE_ALIVE_SECOND);
        RedisOpsUtils.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn(), device, RedisConst.DEVICE_ALIVE_SECOND);
        return true;
    }
@@ -133,7 +130,7 @@
        String deviceSn = payloadReceiverList.stream().findAny().get().getDeviceSn();
        String key = RedisConst.STATE_PAYLOAD_PREFIX + deviceSn;
        // Solve timing problems
        long last = (long) Objects.requireNonNullElse(redisOps.get(key), 0L);
        long last = (long) Objects.requireNonNullElse(RedisOpsUtils.get(key), 0L);
        if (last > timestamp) {
            return;
        }
@@ -153,7 +150,7 @@
        // Save the new payload information.
        boolean isSave = this.savePayloadDTOs(needToSave);
        if (isSave) {
            redisOps.setWithExpire(key, timestamp, RedisConst.DEVICE_ALIVE_SECOND);
            RedisOpsUtils.setWithExpire(key, timestamp, RedisConst.DEVICE_ALIVE_SECOND);
        }
        log.debug("The result of saving the payloads is {}.", isSave);
    }
src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java
@@ -86,9 +86,6 @@
    private ObjectMapper objectMapper;
    @Autowired
    private RedisOpsUtils redisOps;
    @Autowired
    private IWebSocketManageService webSocketManageService;
    @Autowired
@@ -99,7 +96,7 @@
    private ITSAService tsaService;
    private static final List<String> INIT_TOPICS_SUFFIX = List.of(
            OSD_SUF, STATE_SUF, SERVICES_SUF + _REPLY_SUF, REQUESTS_SUF, EVENTS_SUF, PROPERTY_SUF + SET_SUF + _REPLY_SUF);
            OSD_SUF, STATE_SUF, SERVICES_SUF + _REPLY_SUF, EVENTS_SUF, PROPERTY_SUF + SET_SUF + _REPLY_SUF);
    @Override
    public Boolean deviceOffline(StatusGatewayReceiver gateway) {
@@ -109,14 +106,14 @@
        // Only the remote controller is logged in and the aircraft is not connected.
        String key = RedisConst.DEVICE_ONLINE_PREFIX + gatewaySn;
        boolean exist = redisOps.checkExist(key);
        boolean exist = RedisOpsUtils.checkExist(key);
        if (!exist) {
            Optional<DeviceDTO> gatewayOpt = this.getDeviceBySn(gatewaySn);
            if (gatewayOpt.isPresent()) {
                DeviceDTO value = gatewayOpt.get();
                value.setBoundTime(null);
                value.setLoginTime(null);
                redisOps.setWithExpire(key, value, RedisConst.DEVICE_ALIVE_SECOND);
                RedisOpsUtils.setWithExpire(key, value, RedisConst.DEVICE_ALIVE_SECOND);
                this.pushDeviceOnlineTopo(value.getWorkspaceId(), gatewaySn, gatewaySn);
                return true;
            }
@@ -126,7 +123,7 @@
            return firstSaveDevice(gatewayDevice, null);
        }
        DeviceDTO deviceDTO = (DeviceDTO) (redisOps.get(key));
        DeviceDTO deviceDTO = (DeviceDTO) (RedisOpsUtils.get(key));
        String deviceSn = deviceDTO.getChildDeviceSn();
        if (!StringUtils.hasText(deviceSn)) {
            return true;
@@ -140,11 +137,11 @@
        // If no information about this device exists in the cache, the drone is considered to be offline.
        String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn;
        if (!redisOps.checkExist(key) || redisOps.getExpire(key) <= 0) {
        if (!RedisOpsUtils.checkExist(key) || RedisOpsUtils.getExpire(key) <= 0) {
            log.debug("The drone is already offline.");
            return true;
        }
        DeviceDTO device = (DeviceDTO) redisOps.get(key);
        DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(key);
        // Cancel drone-related subscriptions.
        this.unsubscribeTopicOffline(deviceSn);
@@ -152,8 +149,8 @@
        // Publish the latest device topology information in the current workspace.
        this.pushDeviceOfflineTopo(device.getWorkspaceId(), deviceSn);
        redisOps.del(key);
        redisOps.del(RedisConst.OSD_PREFIX + device.getDeviceSn());
        RedisOpsUtils.del(key);
        RedisOpsUtils.del(RedisConst.OSD_PREFIX + device.getDeviceSn());
        log.debug("{} offline.", deviceSn);
        return true;
    }
@@ -163,11 +160,11 @@
        String deviceSn = deviceGateway.getSubDevices().get(0).getSn();
        String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn;
        // change log:  Use redis instead of
        long time = redisOps.getExpire(key);
        long gatewayTime = redisOps.getExpire(RedisConst.DEVICE_ONLINE_PREFIX + deviceGateway.getSn());
        long time = RedisOpsUtils.getExpire(key);
        long gatewayTime = RedisOpsUtils.getExpire(RedisConst.DEVICE_ONLINE_PREFIX + deviceGateway.getSn());
        if (time > 0 && gatewayTime > 0) {
            redisOps.expireKey(key, RedisConst.DEVICE_ALIVE_SECOND);
            RedisOpsUtils.expireKey(key, RedisConst.DEVICE_ALIVE_SECOND);
            DeviceDTO device = DeviceDTO.builder().loginTime(LocalDateTime.now()).deviceSn(deviceSn).build();
            DeviceDTO gateway = DeviceDTO.builder()
                    .loginTime(LocalDateTime.now())
@@ -175,7 +172,7 @@
                    .childDeviceSn(deviceSn).build();
            this.updateDevice(gateway);
            this.updateDevice(device);
            String workspaceId = ((DeviceDTO)(redisOps.get(key))).getWorkspaceId();
            String workspaceId = ((DeviceDTO)(RedisOpsUtils.get(key))).getWorkspaceId();
            if (StringUtils.hasText(workspaceId)) {
                this.subscribeTopicOnline(deviceSn);
                this.subscribeTopicOnline(deviceGateway.getSn());
@@ -314,7 +311,7 @@
        devicesList.forEach(device -> {
            this.spliceDeviceTopo(device);
            device.setWorkspaceId(workspaceId);
            device.setStatus(redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn()));
            device.setStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn()));
        });
        return devicesList;
    }
@@ -368,7 +365,7 @@
        this.getDeviceTopoForPilot(sn)
                .ifPresent(pilotMessage::setData);
        boolean exist = redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + sn);
        boolean exist = RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + sn);
        pilotMessage.getData().setOnlineStatus(exist);
        pilotMessage.getData().setGatewaySn(gatewaySn.equals(sn) ? "" : gatewaySn);
@@ -393,7 +390,7 @@
                            .key(domain + "-" + type + "-" + subType)
                            .build())
                    .iconUrls(device.getIconUrl())
                    .onlineStatus(redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn()))
                    .onlineStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn()))
                    .boundStatus(device.getBoundStatus())
                    .model(device.getDeviceName())
                    .userId(device.getUserId())
@@ -438,9 +435,9 @@
                    topic.indexOf(OSD_SUF));
            // Real-time update of device status in memory
            redisOps.expireKey(RedisConst.DEVICE_ONLINE_PREFIX + from, RedisConst.DEVICE_ALIVE_SECOND);
            RedisOpsUtils.expireKey(RedisConst.DEVICE_ONLINE_PREFIX + from, RedisConst.DEVICE_ALIVE_SECOND);
            DeviceDTO device = (DeviceDTO) redisOps.get(RedisConst.DEVICE_ONLINE_PREFIX + from);
            DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + from);
            if (device == null) {
                Optional<DeviceDTO> deviceOpt = this.getDeviceBySn(from);
@@ -449,9 +446,10 @@
                }
                device = deviceOpt.get();
                if (!StringUtils.hasText(device.getWorkspaceId())) {
                    this.unsubscribeTopicOffline(from);
                    return;
                }
                redisOps.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + from, device,
                RedisOpsUtils.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + from, device,
                        RedisConst.DEVICE_ALIVE_SECOND);
                this.subscribeTopicOnline(from);
            }
@@ -629,7 +627,7 @@
            return deviceDTOBuilder.firmwareStatus(DeviceFirmwareStatusEnum.NOT_UPGRADE.getVal()).build();
        }
        // Query whether the device is updating firmware.
        Object progress = redisOps.get(RedisConst.FIRMWARE_UPGRADING_PREFIX + entity.getDeviceSn());
        Object progress = RedisOpsUtils.get(RedisConst.FIRMWARE_UPGRADING_PREFIX + entity.getDeviceSn());
        if (Objects.nonNull(progress)) {
            return deviceDTOBuilder.firmwareStatus(DeviceFirmwareStatusEnum.UPGRADING.getVal()).firmwareProgress((int)progress).build();
        }
@@ -668,12 +666,12 @@
        }
        String key = RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn();
        DeviceDTO redisDevice = (DeviceDTO)redisOps.get(key);
        DeviceDTO redisDevice = (DeviceDTO)RedisOpsUtils.get(key);
        if (Objects.isNull(redisDevice)) {
            return false;
        }
        redisDevice.setWorkspaceId(device.getWorkspaceId());
        redisOps.setWithExpire(key, redisDevice, RedisConst.DEVICE_ALIVE_SECOND);
        RedisOpsUtils.setWithExpire(key, redisDevice, RedisConst.DEVICE_ALIVE_SECOND);
        if (DeviceDomainEnum.GATEWAY.getDesc().equals(redisDevice.getDomain())) {
            this.pushDeviceOnlineTopo(webSocketManageService.getValueWithWorkspace(device.getWorkspaceId()),
@@ -785,12 +783,12 @@
                        .eq(DeviceEntity::getBoundStatus, true));
        List<DeviceDTO> devicesList = pagination.getRecords().stream().map(this::deviceEntityConvertToDTO)
                .peek(device -> {
                    device.setStatus(redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn()));
                    device.setStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn()));
                    if (StringUtils.hasText(device.getChildDeviceSn())) {
                        Optional<DeviceDTO> childOpt = this.getDeviceBySn(device.getChildDeviceSn());
                        childOpt.ifPresent(child -> {
                            child.setStatus(
                                    redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + child.getDeviceSn()));
                                    RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + child.getDeviceSn()));
                            child.setWorkspaceName(device.getWorkspaceName());
                            device.setChildren(child);
                        });
@@ -803,9 +801,9 @@
    @Override
    public void unbindDevice(String deviceSn) {
        String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn;
        DeviceDTO redisDevice = (DeviceDTO) redisOps.get(key);
        DeviceDTO redisDevice = (DeviceDTO) RedisOpsUtils.get(key);
        redisDevice.setWorkspaceId("");
        redisOps.setWithExpire(key, redisDevice, RedisConst.DEVICE_ALIVE_SECOND);
        RedisOpsUtils.setWithExpire(key, redisDevice, RedisConst.DEVICE_ALIVE_SECOND);
        DeviceDTO device = DeviceDTO.builder()
                .deviceSn(deviceSn)
@@ -823,7 +821,7 @@
            return Optional.empty();
        }
        DeviceDTO device = devicesList.get(0);
        device.setStatus(redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + sn));
        device.setStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + sn));
        return Optional.of(device);
    }
@@ -877,7 +875,7 @@
        }
        // Record the device state that needs to be updated.
        deviceOtaFirmwares.forEach(deviceOta -> redisOps.setWithExpire(
        deviceOtaFirmwares.forEach(deviceOta -> RedisOpsUtils.setWithExpire(
                RedisConst.FIRMWARE_UPGRADING_PREFIX + deviceOta.getSn(),
                bid,
                RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND));
@@ -890,7 +888,7 @@
        if (!dockOnline) {
            throw new RuntimeException("Dock is offline.");
        }
        DeviceDTO deviceDTO = (DeviceDTO) redisOps.get(RedisConst.DEVICE_ONLINE_PREFIX + dockSn);
        DeviceDTO deviceDTO = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + dockSn);
        boolean deviceOnline = this.checkDeviceOnline(deviceDTO.getChildDeviceSn());
        if (!deviceOnline) {
            throw new RuntimeException("Device is offline.");
@@ -904,7 +902,7 @@
        }
        String topic = THING_MODEL_PRE + PRODUCT + dockSn + PROPERTY_SUF + SET_SUF;
        OsdSubDeviceReceiver osd = (OsdSubDeviceReceiver) redisOps.get(RedisConst.OSD_PREFIX + deviceDTO.getChildDeviceSn());
//        OsdSubDeviceReceiver osd = (OsdSubDeviceReceiver) RedisOpsUtils.get(RedisConst.OSD_PREFIX + deviceDTO.getChildDeviceSn());
        if (!param.isObject()) {
            this.deviceOnePropertySet(topic, propertyEnum, Map.entry(propertyEnum.getProperty(), param));
            return;
@@ -912,7 +910,7 @@
        // If there are multiple parameters, set them separately.
        for (Iterator<Map.Entry<String, JsonNode>> filed = param.fields(); filed.hasNext(); ) {
            Map.Entry<String, JsonNode> node = filed.next();
            boolean isPublish = basicDeviceProperty.canPublish(node.getKey(), osd);
            boolean isPublish = basicDeviceProperty.canPublish(node.getKey(), null);
            if (!isPublish) {
                continue;
            }
@@ -954,7 +952,7 @@
    public Boolean checkDeviceOnline(String sn) {
        String key = RedisConst.DEVICE_ONLINE_PREFIX + sn;
        return redisOps.checkExist(key) && redisOps.getExpire(key) > 0;
        return RedisOpsUtils.checkExist(key) && RedisOpsUtils.getExpire(key) > 0;
    }
    /**
@@ -1052,6 +1050,8 @@
            // Set the icon of the gateway device displayed in the pilot's map when it is selected, required in the TSA module.
            device.setUrlSelect(IconUrlEnum.SELECT_PERSON.getUrl());
        }
        deviceOpt.ifPresent(oldDevice -> device.setNickname(oldDevice.getNickname()));
        device.setChildSn(deviceSn);
        device.setLoginTime(System.currentTimeMillis());
@@ -1061,7 +1061,7 @@
        }
        device.setWorkspaceId(saveDeviceOpt.get().getWorkspaceId());
        redisOps.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn(),
        RedisOpsUtils.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn(),
                DeviceDTO.builder()
                        .deviceSn(device.getDeviceSn())
                        .workspaceId(device.getWorkspaceId())
src/main/java/com/dji/sample/manage/service/impl/LiveStreamServiceImpl.java
@@ -53,9 +53,6 @@
    @Autowired
    private IMessageSenderService messageSender;
    @Autowired
    private RedisOpsUtils redisOps;
    @Override
    public List<CapacityDeviceDTO> getLiveCapacity(String workspaceId) {
@@ -68,7 +65,7 @@
        // Query the live capability of each drone.
        return devicesList.stream()
                .filter(device -> redisOps.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn()))
                .filter(device -> RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn()))
                .map(device -> CapacityDeviceDTO.builder()
                        .name(Objects.requireNonNullElse(device.getNickname(), device.getDeviceName()))
                        .sn(device.getDeviceSn())
@@ -82,7 +79,7 @@
        // Solve timing problems
        for (CapacityDeviceReceiver capacityDeviceReceiver : liveCapacityReceiver.getDeviceList()) {
            long last = (long) Objects.requireNonNullElse(
                    redisOps.get(StateDataEnum.LIVE_CAPACITY + RedisConst.DELIMITER + capacityDeviceReceiver.getSn()), 0L);
                    RedisOpsUtils.get(StateDataEnum.LIVE_CAPACITY + RedisConst.DELIMITER + capacityDeviceReceiver.getSn()), 0L);
            if (last > timestamp) {
                return;
            }
@@ -172,7 +169,7 @@
        String respTopic = THING_MODEL_PRE + PRODUCT + responseResult.getData().getDeviceSn() + SERVICES_SUF;
        ServiceReply receiveReply = this.publishLiveSetQuality(respTopic, liveParam);
        if (ResponseResult.CODE_SUCCESS == receiveReply.getResult()) {
        if (ResponseResult.CODE_SUCCESS != receiveReply.getResult()) {
            return ResponseResult.error(LiveErrorEnum.find(receiveReply.getResult()));
        }
src/main/java/com/dji/sample/manage/service/impl/LogsFileServiceImpl.java
@@ -44,9 +44,6 @@
    private OssServiceContext ossService;
    @Autowired
    private OssConfiguration configuration;
    @Autowired
    private OssServiceContext ossServiceContext;
    @Override
@@ -114,7 +111,7 @@
        List<String> fileIds = new ArrayList<>();
        logsFiles.forEach(file -> {
            if (file.getStatus()) {
                ossService.deleteObject(configuration.getBucket(), file.getObjectKey());
                ossService.deleteObject(OssConfiguration.bucket, file.getObjectKey());
            }
            fileIds.add(file.getFileId());
        });
@@ -146,7 +143,7 @@
        if (Objects.isNull(logsFile)) {
            return null;
        }
        return ossService.getObjectUrl(configuration.getBucket(), logsFile.getObjectKey());
        return ossService.getObjectUrl(OssConfiguration.bucket, logsFile.getObjectKey());
    }
    private LogsFileEntity receiver2Entity(LogsExtFileReceiver receiver) {
src/main/java/com/dji/sample/media/controller/FileController.java
@@ -32,7 +32,7 @@
    public ResponseResult<PaginationData<MediaFileDTO>> getFilesList(@RequestParam(defaultValue = "1") Long page,
                               @RequestParam(name = "page_size", defaultValue = "10") Long pageSize,
                               @PathVariable(name = "workspace_id") String workspaceId) {
        PaginationData<MediaFileDTO> filesList = fileService.getJobsPaginationByWorkspaceId(workspaceId, page, pageSize);
        PaginationData<MediaFileDTO> filesList = fileService.getMediaFilesPaginationByWorkspaceId(workspaceId, page, pageSize);
        return ResponseResult.success(filesList);
    }
src/main/java/com/dji/sample/media/model/MediaFileCountDTO.java
New file
@@ -0,0 +1,30 @@
package com.dji.sample.media.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * @author sean
 * @version 1.3
 * @date 2022/11/22
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MediaFileCountDTO {
    private String tid;
    private String bid;
    private String preJobId;
    private String jobId;
    private Integer mediaCount;
    private Integer uploadedCount;
}
src/main/java/com/dji/sample/media/model/MediaFileDTO.java
@@ -39,4 +39,6 @@
    private String fingerprint;
    private LocalDateTime createTime;
    private String jobId;
}
src/main/java/com/dji/sample/media/model/MediaMethodEnum.java
New file
@@ -0,0 +1,20 @@
package com.dji.sample.media.model;
import lombok.Getter;
/**
 * @author sean
 * @version 1.3
 * @date 2022/11/23
 */
@Getter
public enum MediaMethodEnum {
    UPLOAD_FLIGHT_TASK_MEDIA_PRIORITIZE("upload_flighttask_media_prioritize");
    private String method;
    MediaMethodEnum(String method) {
        this.method = method;
    }
}
src/main/java/com/dji/sample/media/service/IFileService.java
@@ -44,7 +44,7 @@
     * @param pageSize
     * @return
     */
    PaginationData<MediaFileDTO> getJobsPaginationByWorkspaceId(String workspaceId, long page, long pageSize);
    PaginationData<MediaFileDTO> getMediaFilesPaginationByWorkspaceId(String workspaceId, long page, long pageSize);
    /**
     * Get the download address of the file.
@@ -53,4 +53,12 @@
     * @return
     */
    URL getObjectUrl(String workspaceId, String fileId);
    /**
     * Query all media files of a job.
     * @param workspaceId
     * @param jobId
     * @return
     */
    List<MediaFileDTO> getFilesByWorkspaceAndJobId(String workspaceId, String jobId);
}
src/main/java/com/dji/sample/media/service/IMediaService.java
@@ -2,6 +2,7 @@
import com.dji.sample.component.mqtt.model.CommonTopicReceiver;
import com.dji.sample.media.model.FileUploadDTO;
import org.springframework.messaging.MessageHeaders;
import java.util.List;
@@ -49,4 +50,11 @@
     * @return
     */
    void handleFileUploadCallBack(CommonTopicReceiver receiver);
    /**
     * Handles the highest priority message about media uploads.
     * @param receiver
     * @param headers
     */
    void handleHighestPriorityUploadFlightTaskMedia(CommonTopicReceiver receiver, MessageHeaders headers);
}
src/main/java/com/dji/sample/media/service/impl/FileServiceImpl.java
@@ -46,9 +46,6 @@
    @Autowired
    private OssServiceContext ossService;
    @Autowired
    private OssConfiguration configuration;
    private Optional<MediaFileEntity> getMediaByFingerprint(String workspaceId, String fingerprint) {
        MediaFileEntity fileEntity = mapper.selectOne(new LambdaQueryWrapper<MediaFileEntity>()
                .eq(MediaFileEntity::getWorkspaceId, workspaceId)
@@ -86,7 +83,7 @@
    }
    @Override
    public PaginationData<MediaFileDTO> getJobsPaginationByWorkspaceId(String workspaceId, long page, long pageSize) {
    public PaginationData<MediaFileDTO> getMediaFilesPaginationByWorkspaceId(String workspaceId, long page, long pageSize) {
        Page<MediaFileEntity> pageData = mapper.selectPage(
                new Page<MediaFileEntity>(page, pageSize),
                new LambdaQueryWrapper<MediaFileEntity>()
@@ -107,7 +104,16 @@
            throw new IllegalArgumentException("{} doesn't exist.");
        }
        return ossService.getObjectUrl(configuration.getBucket(), mediaFileOpt.get().getObjectKey());
        return ossService.getObjectUrl(OssConfiguration.bucket, mediaFileOpt.get().getObjectKey());
    }
    @Override
    public List<MediaFileDTO> getFilesByWorkspaceAndJobId(String workspaceId, String jobId) {
        return mapper.selectList(new LambdaQueryWrapper<MediaFileEntity>()
                .eq(MediaFileEntity::getWorkspaceId, workspaceId)
                .eq(MediaFileEntity::getJobId, jobId))
                .stream()
                .map(this::entityConvertToDto).collect(Collectors.toList());
    }
    /**
@@ -160,7 +166,8 @@
                    .payload(entity.getPayload())
                    .createTime(LocalDateTime.ofInstant(
                            Instant.ofEpochMilli(entity.getCreateTime()), ZoneId.systemDefault()))
                    .drone(entity.getDrone());
                    .drone(entity.getDrone())
                    .jobId(entity.getJobId());
        }
src/main/java/com/dji/sample/media/service/impl/MediaServiceImpl.java
@@ -1,12 +1,21 @@
package com.dji.sample.media.service.impl;
import com.dji.sample.common.error.CommonErrorEnum;
import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.component.mqtt.model.*;
import com.dji.sample.component.mqtt.service.IMessageSenderService;
import com.dji.sample.component.redis.RedisConst;
import com.dji.sample.component.redis.RedisOpsUtils;
import com.dji.sample.component.websocket.model.BizCodeEnum;
import com.dji.sample.component.websocket.model.CustomWebSocketMessage;
import com.dji.sample.component.websocket.service.ISendMessageService;
import com.dji.sample.component.websocket.service.IWebSocketManageService;
import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.dji.sample.manage.service.IDeviceService;
import com.dji.sample.media.model.FileUploadCallback;
import com.dji.sample.media.model.FileUploadDTO;
import com.dji.sample.media.model.MediaFileCountDTO;
import com.dji.sample.media.model.MediaFileDTO;
import com.dji.sample.media.service.IFileService;
import com.dji.sample.media.service.IMediaService;
@@ -15,9 +24,13 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -43,6 +56,12 @@
    @Autowired
    private IDeviceService deviceService;
    @Autowired
    private ISendMessageService sendMessageService;
    @Autowired
    private IWebSocketManageService webSocketManageService;
    @Override
    public Boolean fastUpload(String workspaceId, String fingerprint) {
@@ -79,32 +98,126 @@
        String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + receiver.getGateway()
                + TopicConst.EVENTS_SUF + TopicConst._REPLY_SUF;
        CommonTopicResponse<Object> data = CommonTopicResponse.builder()
        CommonTopicResponse<RequestsReply> data = CommonTopicResponse.<RequestsReply>builder()
                .timestamp(System.currentTimeMillis())
                .method(EventsMethodEnum.FILE_UPLOAD_CALLBACK.getMethod())
                .data(RequestsReply.success())
                .tid(receiver.getTid())
                .bid(receiver.getBid())
                .build();
        if (callback.getResult() == ResponseResult.CODE_SUCCESS) {
            String jobId = callback.getFile().getExt().getFlightId();
            Optional<WaylineJobDTO> jobOpt = waylineJobService.getJobByJobId(jobId);
            if (jobOpt.isPresent()) {
                // Set the drone sn that shoots the media
                Optional<DeviceDTO> dockDTO = deviceService.getDeviceBySn(jobOpt.get().getDockSn());
                dockDTO.ifPresent(dock -> callback.getFile().getExt().setSn(dock.getChildDeviceSn()));
                // set path
                String objectKey = callback.getFile().getObjectKey();
                callback.getFile().setPath(objectKey.substring(objectKey.indexOf("/") + 1, objectKey.lastIndexOf("/")));
        if (callback.getResult() != ResponseResult.CODE_SUCCESS) {
            messageSenderService.publish(topic, data);
            return;
        }
                int id = fileService.saveFile(jobOpt.get().getWorkspaceId(), callback.getFile());
                if (id <= 0) {
                    data.setData(ResponseResult.error());
                }
        String jobId = callback.getFile().getExt().getFlightId();
        MediaFileCountDTO mediaFileCount = (MediaFileCountDTO) RedisOpsUtils.hashGet(RedisConst.MEDIA_FILE_PREFIX + receiver.getGateway(), jobId);
        // duplicate data
        if (receiver.getBid().equals(mediaFileCount.getBid()) && receiver.getTid().equals(mediaFileCount.getTid())) {
            System.out.println("相同" + receiver.getBid() + "\t tid:" + receiver.getTid());
            messageSenderService.publish(topic, data);
            return;
        }
        Optional<WaylineJobDTO> jobOpt = waylineJobService.getJobByJobId(jobId);
        if (jobOpt.isPresent()) {
            boolean isSave = parseMediaFile(callback, jobOpt.get());
            if (!isSave) {
                data.setData(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT));
            }
        }
        messageSenderService.publish(topic, data);
        notifyUploadedCount(mediaFileCount, receiver, jobId);
    }
    /**
     * update the uploaded count and notify web side
     * @param mediaFileCount
     * @param receiver
     * @param jobId
     */
    private void notifyUploadedCount(MediaFileCountDTO mediaFileCount, CommonTopicReceiver receiver, String jobId) {
        mediaFileCount.setBid(receiver.getBid());
        mediaFileCount.setTid(receiver.getTid());
        mediaFileCount.setUploadedCount(mediaFileCount.getUploadedCount() + 1);
        String key = RedisConst.MEDIA_FILE_PREFIX + receiver.getGateway();
        // After all the files of the job are uploaded, delete the media file key.
        if (mediaFileCount.getUploadedCount() >= mediaFileCount.getMediaCount()) {
            RedisOpsUtils.hashDel(key, new String[]{jobId});
            // After uploading, delete the key with the highest priority.
            String highestKey = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + receiver.getGateway();
            if (jobId.equals(Objects.requireNonNullElse(RedisOpsUtils.get(highestKey), ""))) {
                RedisOpsUtils.del(highestKey);
            }
            if (RedisOpsUtils.hashLen(key) == 0) {
                RedisOpsUtils.del(key);
            }
        } else {
            RedisOpsUtils.hashSet(key, jobId, mediaFileCount);
        }
        DeviceDTO dock = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway());
        sendMessageService.sendBatch(webSocketManageService.getValueWithWorkspaceAndUserType(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal()),
                CustomWebSocketMessage.builder()
                        .bizCode(BizCodeEnum.FILE_UPLOAD_CALLBACK.getCode())
                        .timestamp(System.currentTimeMillis())
                        .data(mediaFileCount)
                        .build());
    }
    private Boolean parseMediaFile(FileUploadCallback callback, WaylineJobDTO job) {
        // Set the drone sn that shoots the media
        Optional<DeviceDTO> dockDTO = deviceService.getDeviceBySn(job.getDockSn());
        dockDTO.ifPresent(dock -> callback.getFile().getExt().setSn(dock.getChildDeviceSn()));
        // set path
        String objectKey = callback.getFile().getObjectKey();
        callback.getFile().setPath(objectKey.substring(objectKey.indexOf("/") + 1, objectKey.lastIndexOf("/")));
        return fileService.saveFile(job.getWorkspaceId(), callback.getFile()) > 0;
    }
    @Override
    @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA, outputChannel = ChannelName.OUTBOUND)
    public void handleHighestPriorityUploadFlightTaskMedia(CommonTopicReceiver receiver, MessageHeaders headers) {
        Map map = objectMapper.convertValue(receiver.getData(), Map.class);
        if (map.isEmpty() || !map.containsKey(MapKeyConst.FLIGHT_ID)) {
            return;
        }
        String dockSn = receiver.getGateway();
        String jobId = map.get(MapKeyConst.FLIGHT_ID).toString();
        String key = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + dockSn;
        Object preJobId = RedisOpsUtils.get(key);
        RedisOpsUtils.setWithExpire(key, jobId,
                RedisConst.DEVICE_ALIVE_SECOND * 5);
        DeviceDTO dock = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + dockSn);
        sendMessageService.sendBatch(webSocketManageService.getValueWithWorkspaceAndUserType(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal()),
                CustomWebSocketMessage.builder()
                        .timestamp(System.currentTimeMillis())
                        .bizCode(BizCodeEnum.HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA.getCode())
                        .data(MediaFileCountDTO.builder()
                                .preJobId(Objects.nonNull(preJobId) ? preJobId.toString() : null)
                                .jobId(jobId).build())
                        .build());
        messageSenderService.publish(headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF,
                CommonTopicResponse.builder()
                        .data(RequestsReply.success())
                        .method(receiver.getMethod())
                        .timestamp(System.currentTimeMillis())
                        .bid(receiver.getBid())
                        .tid(receiver.getTid())
                        .build());
    }
}
src/main/java/com/dji/sample/storage/service/impl/StorageServiceImpl.java
@@ -26,18 +26,15 @@
    @Autowired
    private OssServiceContext ossService;
    @Autowired
    private OssConfiguration configuration;
    @Override
    public StsCredentialsDTO getSTSCredentials() {
        return StsCredentialsDTO.builder()
                .endpoint(configuration.getEndpoint())
                .bucket(configuration.getBucket())
                .endpoint(OssConfiguration.endpoint)
                .bucket(OssConfiguration.bucket)
                .credentials(ossService.getCredentials())
                .provider(configuration.getProvider())
                .objectKeyPrefix(configuration.getObjectDirPrefix())
                .region(configuration.getRegion())
                .provider(OssConfiguration.provider)
                .objectKeyPrefix(OssConfiguration.objectDirPrefix)
                .region(OssConfiguration.region)
                .build();
    }
src/main/java/com/dji/sample/wayline/controller/WaylineFileController.java
@@ -75,6 +75,7 @@
        try {
            URL url = waylineFileService.getObjectUrl(workspaceId, waylineId);
            response.sendRedirect(url.toString());
        } catch (IOException | SQLException e) {
            e.printStackTrace();
        }
src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java
@@ -73,4 +73,17 @@
        waylineJobService.cancelFlightTask(workspaceId, jobIds);
        return ResponseResult.success();
    }
    /**
     * Set the media files for this job to upload immediately.
     * @param workspaceId
     * @param jobId
     * @return
     */
    @PostMapping("/{workspace_id}/jobs/{job_id}/media-highest")
    public ResponseResult uploadMediaHighestPriority(@PathVariable(name = "workspace_id") String workspaceId,
                                             @PathVariable(name = "job_id") String jobId) {
        waylineJobService.uploadMediaHighestPriority(workspaceId, jobId);
        return ResponseResult.success();
    }
}
src/main/java/com/dji/sample/wayline/model/dto/WaylineJobDTO.java
@@ -53,4 +53,8 @@
    private Integer outOfControlAction;
    private Integer mediaCount;
    private Integer uploadedCount;
    private Boolean uploading;
}
src/main/java/com/dji/sample/wayline/service/IWaylineJobService.java
@@ -80,4 +80,11 @@
     * @param headers
     */
    void flightTaskResourceGet(CommonTopicReceiver receiver, MessageHeaders headers);
    /**
     * Set the media files for this job to upload immediately.
     * @param workspaceId
     * @param jobId
     */
    void uploadMediaHighestPriority(String workspaceId, String jobId);
}
src/main/java/com/dji/sample/wayline/service/impl/FlightTaskServiceImpl.java
@@ -11,6 +11,7 @@
import com.dji.sample.component.websocket.service.IWebSocketManageService;
import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.dji.sample.media.model.MediaFileCountDTO;
import com.dji.sample.wayline.model.dto.FlightTaskProgressReceiver;
import com.dji.sample.wayline.model.dto.WaylineJobDTO;
import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum;
@@ -53,9 +54,6 @@
    private IWebSocketManageService webSocketManageService;
    @Autowired
    private RedisOpsUtils redisOps;
    @Autowired
    private IWaylineJobService waylineJobService;
    @Override
@@ -83,17 +81,23 @@
                    .mediaCount(output.getExt().getMediaCount())
                    .build();
            // record the update of the media count.
            if (Objects.nonNull(job.getMediaCount())) {
                RedisOpsUtils.hashSet(RedisConst.MEDIA_FILE_PREFIX + receiver.getGateway(), job.getJobId(),
                        MediaFileCountDTO.builder().jobId(receiver.getBid()).mediaCount(job.getMediaCount()).uploadedCount(0).build());
            }
            if (EventsResultStatusEnum.OK != statusEnum) {
                job.setCode(eventsReceiver.getResult());
                job.setStatus(WaylineJobStatusEnum.FAILED.getVal());
            }
            waylineJobService.updateJob(job);
            redisOps.del(receiver.getBid());
            RedisOpsUtils.del(receiver.getBid());
        }
        redisOps.setWithExpire(receiver.getBid(), eventsReceiver, RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND);
        RedisOpsUtils.setWithExpire(receiver.getBid(), eventsReceiver, RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND);
        DeviceDTO device = (DeviceDTO) redisOps.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway());
        DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway());
        websocketMessageService.sendBatch(
                webSocketManageService.getValueWithWorkspaceAndUserType(
                        device.getWorkspaceId(), UserTypeEnum.WEB.getVal()),
@@ -118,19 +122,19 @@
    @Scheduled(initialDelay = 10, fixedRate = 5, timeUnit = TimeUnit.SECONDS)
    private void checkScheduledJob() {
        Object jobIdValue = redisOps.zGetMin(RedisConst.WAYLINE_JOB);
        Object jobIdValue = RedisOpsUtils.zGetMin(RedisConst.WAYLINE_JOB);
        log.info("Check the timed jobs of the wayline. {}", jobIdValue);
        if (Objects.isNull(jobIdValue)) {
            return;
        }
        String jobId = String.valueOf(jobIdValue);
        double time = redisOps.zScore(RedisConst.WAYLINE_JOB, jobIdValue);
        double time = RedisOpsUtils.zScore(RedisConst.WAYLINE_JOB, jobIdValue);
        long now = System.currentTimeMillis();
        int offset = 30_000;
        // Expired tasks are deleted directly.
        if (time < now - offset) {
            redisOps.zRemove(RedisConst.WAYLINE_JOB, jobId);
            RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB, jobId);
            waylineJobService.updateJob(WaylineJobDTO.builder()
                    .jobId(jobId)
                    .status(WaylineJobStatusEnum.FAILED.getVal())
@@ -150,7 +154,7 @@
                        .endTime(LocalDateTime.now())
                        .code(HttpStatus.SC_INTERNAL_SERVER_ERROR).build());
            } finally {
                redisOps.zRemove(RedisConst.WAYLINE_JOB, jobId);
                RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB, jobId);
            }
        }
    }
src/main/java/com/dji/sample/wayline/service/impl/WaylineFileServiceImpl.java
@@ -53,9 +53,6 @@
    @Autowired
    private OssServiceContext ossService;
    @Autowired
    private OssConfiguration configuration;
    @Override
    public PaginationData<WaylineFileDTO> getWaylinesByParam(String workspaceId, WaylineQueryParam param) {
        // Paging Query
@@ -97,7 +94,7 @@
        if (waylineOpt.isEmpty()) {
            throw new SQLException(waylineId + " does not exist.");
        }
        return ossService.getObjectUrl(configuration.getBucket(), waylineOpt.get().getObjectKey());
        return ossService.getObjectUrl(OssConfiguration.bucket, waylineOpt.get().getObjectKey());
    }
    @Override
@@ -107,10 +104,10 @@
        file.setWorkspaceId(workspaceId);
        if (!StringUtils.hasText(file.getSign())) {
            try (InputStream object = ossService.getObject(configuration.getBucket(), metadata.getObjectKey())) {
            try (InputStream object = ossService.getObject(OssConfiguration.bucket, metadata.getObjectKey())) {
                if (object.available() == 0) {
                    throw new RuntimeException("The file " + metadata.getObjectKey() +
                            " does not exist in the bucket[" + configuration.getBucket() + "].");
                            " does not exist in the bucket[" + OssConfiguration.bucket + "].");
                }
                file.setSign(DigestUtils.md5DigestAsHex(object));
            } catch (IOException e) {
@@ -159,7 +156,7 @@
        if (!isDel) {
            return false;
        }
        return ossService.deleteObject(configuration.getBucket(), wayline.getObjectKey());
        return ossService.deleteObject(OssConfiguration.bucket, wayline.getObjectKey());
    }
    @Override
@@ -174,7 +171,7 @@
            waylineFile.setWaylineId(workspaceId);
            waylineFile.setUsername(creator);
            ossService.putObject(configuration.getBucket(), waylineFile.getObjectKey(), file.getInputStream());
            ossService.putObject(OssConfiguration.bucket, waylineFile.getObjectKey(), file.getInputStream());
            this.saveWaylineFile(workspaceId, waylineFile);
        } catch (IOException e) {
            e.printStackTrace();
@@ -222,7 +219,7 @@
                return Optional.of(WaylineFileDTO.builder()
                        .droneModelKey(String.format("%s-%s-%s", DeviceDomainEnum.SUB_DEVICE.getVal(), type, subType))
                        .payloadModelKeys(List.of(String.format("%s-%s-%s",DeviceDomainEnum.PAYLOAD.getVal(), payloadType, payloadSubType)))
                        .objectKey(configuration.getObjectDirPrefix() + File.separator + filename)
                        .objectKey(OssConfiguration.objectDirPrefix + File.separator + filename)
                        .name(filename.substring(0, filename.lastIndexOf(WAYLINE_FILE_SUFFIX)))
                        .sign(DigestUtils.md5DigestAsHex(file.getInputStream()))
                        .templateTypes(List.of(Integer.parseInt(templateId)))
src/main/java/com/dji/sample/wayline/service/impl/WaylineJobServiceImpl.java
@@ -14,6 +14,9 @@
import com.dji.sample.component.redis.RedisOpsUtils;
import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.service.IDeviceService;
import com.dji.sample.media.model.MediaFileCountDTO;
import com.dji.sample.media.model.MediaMethodEnum;
import com.dji.sample.media.service.IFileService;
import com.dji.sample.wayline.dao.IWaylineJobMapper;
import com.dji.sample.wayline.model.dto.*;
import com.dji.sample.wayline.model.entity.WaylineJobEntity;
@@ -66,11 +69,10 @@
    private IMessageSenderService messageSender;
    @Autowired
    private RedisOpsUtils redisOps;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private IFileService fileService;
    @Override
    public Optional<WaylineJobDTO> createWaylineJob(CreateJobParam param, CustomClaim customClaim) {
@@ -94,6 +96,7 @@
                .waylineType(param.getWaylineType())
                .outOfControlAction(param.getOutOfControlAction())
                .rthAltitude(param.getRthAltitude())
                .mediaCount(0)
                .build();
        int id = mapper.insert(jobEntity);
        if (id <= 0) {
@@ -167,7 +170,7 @@
        }
        if (WaylineTaskTypeEnum.TIMED.getVal() == waylineJob.getTaskType()) {
            boolean isAdd = redisOps.zAdd(RedisConst.WAYLINE_JOB, waylineJob.getJobId(),
            boolean isAdd = RedisOpsUtils.zAdd(RedisConst.WAYLINE_JOB, waylineJob.getJobId(),
                    waylineJob.getExecuteTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
            if (!isAdd) {
                return ResponseResult.error("Failed to create scheduled job.");
@@ -218,7 +221,7 @@
                .jobId(jobId)
                .status(WaylineJobStatusEnum.IN_PROGRESS.getVal())
                .build());
        redisOps.setWithExpire(jobId,
        RedisOpsUtils.setWithExpire(jobId,
                EventsReceiver.<FlightTaskProgressReceiver>builder().bid(jobId).sn(job.getDockSn()).build(),
                RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND);
        return true;
@@ -256,7 +259,7 @@
    private void publishCancelTask(String workspaceId, String dockSn, List<String> jobIds) {
        boolean isOnline = deviceService.checkDeviceOnline(dockSn);
        if (isOnline) {
        if (!isOnline) {
            throw new RuntimeException("Dock is offline.");
        }
        String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + dockSn + TopicConst.SERVICES_SUF;
@@ -282,7 +285,7 @@
                    .status(WaylineJobStatusEnum.CANCEL.getVal())
                    .endTime(LocalDateTime.now())
                    .build());
            redisOps.zRemove(RedisConst.WAYLINE_JOB, jobId);
            RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB, jobId);
        }
    }
@@ -372,6 +375,33 @@
    }
    @Override
    public void uploadMediaHighestPriority(String workspaceId, String jobId) {
        Optional<WaylineJobDTO> jobOpt = getJobByJobId(jobId);
        if (jobOpt.isEmpty()) {
            throw new RuntimeException(CommonErrorEnum.ILLEGAL_ARGUMENT.getErrorMsg());
        }
        String dockSn = jobOpt.get().getDockSn();
        String key = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + dockSn;
        if (RedisOpsUtils.checkExist(key) && jobId.equals(RedisOpsUtils.get(key).toString())) {
            return;
        }
        ServiceReply reply = messageSender.publishWithReply(TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + dockSn + TopicConst.SERVICES_SUF,
                CommonTopicResponse.builder()
                        .tid(UUID.randomUUID().toString())
                        .bid(UUID.randomUUID().toString())
                        .timestamp(System.currentTimeMillis())
                        .method(MediaMethodEnum.UPLOAD_FLIGHT_TASK_MEDIA_PRIORITIZE.getMethod())
                        .data(Map.of(MapKeyConst.FLIGHT_ID, jobId))
                        .build());
        if (ResponseResult.CODE_SUCCESS != reply.getResult()) {
            throw new RuntimeException("Failed to set media job upload priority. Error Code: " + reply.getResult());
        }
        RedisOpsUtils.setWithExpire(key, jobId, RedisConst.DEVICE_ALIVE_SECOND * 5);
    }
    private WaylineJobEntity dto2Entity(WaylineJobDTO dto) {
        WaylineJobEntity.WaylineJobEntityBuilder builder = WaylineJobEntity.builder();
        if (dto == null) {
@@ -418,12 +448,37 @@
        if (Objects.nonNull(entity.getEndTime())) {
            builder.endTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getEndTime()), ZoneId.systemDefault()));
        }
        if (WaylineJobStatusEnum.IN_PROGRESS.getVal() == entity.getStatus() && redisOps.getExpire(entity.getJobId()) > 0) {
            EventsReceiver<FlightTaskProgressReceiver> taskProgress = (EventsReceiver<FlightTaskProgressReceiver>) redisOps.get(entity.getJobId());
        if (WaylineJobStatusEnum.IN_PROGRESS.getVal() == entity.getStatus() && RedisOpsUtils.getExpire(entity.getJobId()) > 0) {
            EventsReceiver<FlightTaskProgressReceiver> taskProgress = (EventsReceiver<FlightTaskProgressReceiver>) RedisOpsUtils.get(entity.getJobId());
            if (Objects.nonNull(taskProgress.getOutput()) && Objects.nonNull(taskProgress.getOutput().getProgress())) {
                builder.progress(taskProgress.getOutput().getProgress().getPercent());
            }
        }
        if (entity.getMediaCount() == 0) {
            return builder.build();
        }
        // sync the number of media files
        String key = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + entity.getDockSn();
        String countKey = RedisConst.MEDIA_FILE_PREFIX + entity.getDockSn();
        Object mediaFileCount = RedisOpsUtils.hashGet(countKey, entity.getJobId());
        if (Objects.nonNull(mediaFileCount)) {
            builder.uploadedCount(((MediaFileCountDTO) mediaFileCount).getUploadedCount())
                    .uploading(RedisOpsUtils.checkExist(key) && entity.getJobId().equals(RedisOpsUtils.get(key)));
            return builder.build();
        }
        int uploadedSize = fileService.getFilesByWorkspaceAndJobId(entity.getWorkspaceId(), entity.getJobId()).size();
        // All media for this job have been uploaded.
        if (uploadedSize >= entity.getMediaCount()) {
            return builder.uploadedCount(uploadedSize).build();
        }
        RedisOpsUtils.hashSet(countKey, entity.getJobId(),
                MediaFileCountDTO.builder()
                        .jobId(entity.getJobId())
                        .mediaCount(entity.getMediaCount())
                        .uploadedCount(uploadedSize).build());
        return builder.build();
    }
}
src/main/resources/application.yml
@@ -27,6 +27,10 @@
        max-idle: 8
        min-idle: 0
  servlet:
    multipart:
      max-file-size: 2GB
      max-request-size: 2GB
jwt:
  issuer: DJI
src/main/resources/hms.json
Diff too large