Browse Source

1.0.0 initial

v1.1.0
sean.zhou 3 years ago
parent
commit
17835b967b
  1. 38
      .gitignore
  2. 148
      pom.xml
  3. 324
      sql/cloud_sample.sql
  4. 17
      src/main/java/com/dji/sample/CloudApiSampleApplication.java
  5. 40
      src/main/java/com/dji/sample/common/error/CommonErrorEnum.java
  6. 22
      src/main/java/com/dji/sample/common/error/IErrorInfo.java
  7. 78
      src/main/java/com/dji/sample/common/error/LiveErrorEnum.java
  8. 88
      src/main/java/com/dji/sample/common/model/CustomClaim.java
  9. 36
      src/main/java/com/dji/sample/common/model/Pagination.java
  10. 27
      src/main/java/com/dji/sample/common/model/PaginationData.java
  11. 69
      src/main/java/com/dji/sample/common/model/ResponseResult.java
  12. 103
      src/main/java/com/dji/sample/common/util/JwtUtil.java
  13. 40
      src/main/java/com/dji/sample/component/ApplicationBootInitial.java
  14. 60
      src/main/java/com/dji/sample/component/AuthInterceptor.java
  15. 35
      src/main/java/com/dji/sample/component/CorsFilter.java
  16. 33
      src/main/java/com/dji/sample/component/GlobalExceptionHandler.java
  17. 58
      src/main/java/com/dji/sample/component/GlobalScheduleService.java
  18. 95
      src/main/java/com/dji/sample/component/mqtt/config/InboundMessageRouter.java
  19. 63
      src/main/java/com/dji/sample/component/mqtt/config/MqttConfiguration.java
  20. 69
      src/main/java/com/dji/sample/component/mqtt/config/MqttInboundConfiguration.java
  21. 90
      src/main/java/com/dji/sample/component/mqtt/config/MqttMessageChannel.java
  22. 47
      src/main/java/com/dji/sample/component/mqtt/config/MqttOutboundConfiguration.java
  23. 46
      src/main/java/com/dji/sample/component/mqtt/model/ChannelName.java
  24. 29
      src/main/java/com/dji/sample/component/mqtt/model/CommonTopicReceiver.java
  25. 33
      src/main/java/com/dji/sample/component/mqtt/model/CommonTopicResponse.java
  26. 29
      src/main/java/com/dji/sample/component/mqtt/model/TopicConst.java
  27. 29
      src/main/java/com/dji/sample/component/mqtt/model/TopicStateReceiver.java
  28. 27
      src/main/java/com/dji/sample/component/mqtt/service/IMessageSenderService.java
  29. 33
      src/main/java/com/dji/sample/component/mqtt/service/IMqttMessageGateway.java
  30. 38
      src/main/java/com/dji/sample/component/mqtt/service/IMqttTopicService.java
  31. 50
      src/main/java/com/dji/sample/component/mqtt/service/impl/MessageSenderServiceImpl.java
  32. 44
      src/main/java/com/dji/sample/component/mqtt/service/impl/MqttTopicServiceImpl.java
  33. 24
      src/main/java/com/dji/sample/component/mybatis/MybatisPlusConfiguration.java
  34. 37
      src/main/java/com/dji/sample/component/mybatis/MybatisPlusMetaObjectHandler.java
  35. 104
      src/main/java/com/dji/sample/component/oss/model/AliyunOSSConfiguration.java
  36. 93
      src/main/java/com/dji/sample/component/oss/model/MinIOConfiguration.java
  37. 27
      src/main/java/com/dji/sample/component/oss/service/IOssService.java
  38. 68
      src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java
  39. 66
      src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java
  40. 70
      src/main/java/com/dji/sample/component/websocket/config/AuthPrincipalHandler.java
  41. 25
      src/main/java/com/dji/sample/component/websocket/config/ConcurrentWebSocketSession.java
  42. 20
      src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultFactory.java
  43. 60
      src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultHandler.java
  44. 39
      src/main/java/com/dji/sample/component/websocket/config/WebSocketMessageConfiguration.java
  45. 37
      src/main/java/com/dji/sample/component/websocket/model/BizCodeEnum.java
  46. 30
      src/main/java/com/dji/sample/component/websocket/model/CustomWebSocketMessage.java
  47. 112
      src/main/java/com/dji/sample/component/websocket/model/WebSocketManager.java
  48. 28
      src/main/java/com/dji/sample/component/websocket/service/ISendMessageService.java
  49. 72
      src/main/java/com/dji/sample/component/websocket/service/impl/SendMessageServiceImpl.java
  50. 35
      src/main/java/com/dji/sample/configuration/GlobalMVCConfigurer.java
  51. 42
      src/main/java/com/dji/sample/configuration/GlobalThreadPoolConfiguration.java
  52. 138
      src/main/java/com/dji/sample/manage/controller/DeviceController.java
  53. 66
      src/main/java/com/dji/sample/manage/controller/DevicePayloadController.java
  54. 113
      src/main/java/com/dji/sample/manage/controller/LiveStreamController.java
  55. 47
      src/main/java/com/dji/sample/manage/controller/LoginController.java
  56. 40
      src/main/java/com/dji/sample/manage/controller/TopologyController.java
  57. 29
      src/main/java/com/dji/sample/manage/controller/UserController.java
  58. 41
      src/main/java/com/dji/sample/manage/controller/WorkspaceController.java
  59. 12
      src/main/java/com/dji/sample/manage/dao/ICameraVideoMapper.java
  60. 12
      src/main/java/com/dji/sample/manage/dao/ICapacityCameraMapper.java
  61. 13
      src/main/java/com/dji/sample/manage/dao/IDeviceDictionaryMapper.java
  62. 14
      src/main/java/com/dji/sample/manage/dao/IDeviceMapper.java
  63. 12
      src/main/java/com/dji/sample/manage/dao/IDevicePayloadMapper.java
  64. 8
      src/main/java/com/dji/sample/manage/dao/IUserMapper.java
  65. 8
      src/main/java/com/dji/sample/manage/dao/IWorkspaceMapper.java
  66. 35
      src/main/java/com/dji/sample/manage/handler/AbstractStateTopicHandler.java
  67. 25
      src/main/java/com/dji/sample/manage/handler/StateDefaultHandler.java
  68. 34
      src/main/java/com/dji/sample/manage/handler/StateDeviceBasicHandler.java
  69. 39
      src/main/java/com/dji/sample/manage/handler/StateLiveCapacityHandler.java
  70. 55
      src/main/java/com/dji/sample/manage/handler/StatePayloadHandler.java
  71. 92
      src/main/java/com/dji/sample/manage/handler/StateRouter.java
  72. 59
      src/main/java/com/dji/sample/manage/handler/StateSplitter.java
  73. 70
      src/main/java/com/dji/sample/manage/handler/StatusRouter.java
  74. 45
      src/main/java/com/dji/sample/manage/model/Chan.java
  75. 20
      src/main/java/com/dji/sample/manage/model/DeviceStatusManager.java
  76. 34
      src/main/java/com/dji/sample/manage/model/dto/CapacityCameraDTO.java
  77. 26
      src/main/java/com/dji/sample/manage/model/dto/CapacityDeviceDTO.java
  78. 24
      src/main/java/com/dji/sample/manage/model/dto/CapacityVideoDTO.java
  79. 47
      src/main/java/com/dji/sample/manage/model/dto/DeviceDTO.java
  80. 28
      src/main/java/com/dji/sample/manage/model/dto/DeviceDictionaryDTO.java
  81. 27
      src/main/java/com/dji/sample/manage/model/dto/DeviceModelDTO.java
  82. 29
      src/main/java/com/dji/sample/manage/model/dto/DevicePayloadDTO.java
  83. 25
      src/main/java/com/dji/sample/manage/model/dto/IconUrlDTO.java
  84. 20
      src/main/java/com/dji/sample/manage/model/dto/LiveDTO.java
  85. 26
      src/main/java/com/dji/sample/manage/model/dto/LiveTypeDTO.java
  86. 20
      src/main/java/com/dji/sample/manage/model/dto/LiveUrlAgoraDTO.java
  87. 27
      src/main/java/com/dji/sample/manage/model/dto/LiveUrlGB28181DTO.java
  88. 18
      src/main/java/com/dji/sample/manage/model/dto/LiveUrlRTSPDTO.java
  89. 22
      src/main/java/com/dji/sample/manage/model/dto/TelemetryDTO.java
  90. 32
      src/main/java/com/dji/sample/manage/model/dto/TelemetryDeviceDTO.java
  91. 24
      src/main/java/com/dji/sample/manage/model/dto/TopologyDTO.java
  92. 35
      src/main/java/com/dji/sample/manage/model/dto/TopologyDeviceDTO.java
  93. 37
      src/main/java/com/dji/sample/manage/model/dto/UserDTO.java
  94. 14
      src/main/java/com/dji/sample/manage/model/dto/UserLoginDTO.java
  95. 33
      src/main/java/com/dji/sample/manage/model/dto/WorkspaceDTO.java
  96. 37
      src/main/java/com/dji/sample/manage/model/entity/CameraVideoEntity.java
  97. 46
      src/main/java/com/dji/sample/manage/model/entity/CapacityCameraEntity.java
  98. 45
      src/main/java/com/dji/sample/manage/model/entity/DeviceDictionaryEntity.java
  99. 69
      src/main/java/com/dji/sample/manage/model/entity/DeviceEntity.java
  100. 56
      src/main/java/com/dji/sample/manage/model/entity/DevicePayloadEntity.java
  101. Some files were not shown because too many files have changed in this diff Show More

38
.gitignore vendored

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
/.mvn/
/mvnw
/mvnw.cmd
/logs/
/src/test/

148
pom.xml

@ -0,0 +1,148 @@ @@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.11</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dji</groupId>
<artifactId>cloud-api-sample</artifactId>
<version>1.0.0</version>
<name>cloud-api-sample</name>
<properties>
<java.version>11</java.version>
<mybatis-plus.version>3.4.2</mybatis-plus.version>
<druid.version>1.2.6</druid.version>
<jwt.version>3.12.1</jwt.version>
<mqtt.version>5.5.5</mqtt.version>
<minio.version>8.3.7</minio.version>
<okhttp3.version>4.9.1</okhttp3.version>
<aliyun-sdk-sts.version>3.1.0</aliyun-sdk-sts.version>
<aliyun-oss.version>3.12.0</aliyun-oss.version>
<javax-activation.version>1.1.1</javax-activation.version>
<glassfish-jaxb.version>2.3.3</glassfish-jaxb.version>
<log4j2.version>2.15.0</log4j2.version>
<javax-jaxb.version>2.3.0</javax-jaxb.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
<version>${mqtt.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp3.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-sts</artifactId>
<version>${aliyun-sdk-sts.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun-oss.version}</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>${javax-activation.version}</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>${glassfish-jaxb.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

324
sql/cloud_sample.sql

@ -0,0 +1,324 @@ @@ -0,0 +1,324 @@
CREATE DATABASE IF NOT EXISTS `cloud_sample` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `cloud_sample`;
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
SET NAMES utf8mb4;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE='NO_AUTO_VALUE_ON_ZERO', SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
# manage_camera_video
# ------------------------------------------------------------
DROP TABLE IF EXISTS `manage_camera_video`;
CREATE TABLE `manage_camera_video` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`camera_id` int NOT NULL,
`video_index` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`video_type` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
# manage_capacity_camera
# ------------------------------------------------------------
DROP TABLE IF EXISTS `manage_capacity_camera`;
CREATE TABLE `manage_capacity_camera` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`device_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined',
`description` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`camera_index` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`coexist_video_number_max` int NOT NULL,
`available_video_number` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
# manage_device
# ------------------------------------------------------------
DROP TABLE IF EXISTS `manage_device`;
CREATE TABLE `manage_device` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`device_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`device_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined',
`workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`device_type` smallint NOT NULL,
`sub_type` smallint NOT NULL,
`domain` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`version` smallint NOT NULL,
`device_index` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`child_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`create_time` bigint NOT NULL,
`update_time` bigint NOT NULL,
`device_desc` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`url_normal` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`url_select` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `product_sn_UNIQUE` (`device_sn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
# manage_device_dictionary
# ------------------------------------------------------------
DROP TABLE IF EXISTS `manage_device_dictionary`;
CREATE TABLE `manage_device_dictionary` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`domain` int NOT NULL,
`device_type` int NOT NULL,
`sub_type` int NOT NULL,
`device_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`device_desc` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
LOCK TABLES `manage_device_dictionary` WRITE;
/*!40000 ALTER TABLE `manage_device_dictionary` DISABLE KEYS */;
INSERT INTO `manage_device_dictionary` (`id`, `domain`, `device_type`, `sub_type`, `device_name`, `device_desc`)
VALUES
(1,0,60,0,'Matrice 300 RTK',NULL),
(16,2,56,0,'DJI Smart Controller','Remote control for M300'),
(17,1,20,0,'Z30',NULL),
(18,1,26,0,'XT2',NULL),
(19,1,39,0,'FPV',NULL),
(20,1,41,0,'XTS',NULL),
(21,1,42,0,'H20',NULL),
(22,1,43,0,'H20T',NULL),
(24,1,90742,0,'L1',NULL),
(27,1,50,0,'P1 24mm lens',NULL),
(28,1,50,1,'P1 35mm lens',NULL),
(29,1,50,2,'P1 50mm lens',NULL),
(30,0,67,0,'Matrice 30',NULL),
(31,0,67,1,'Matrice 30T',NULL),
(32,2,119,0,'DJI RC Plus','Remote control for M30'),
(33,1,52,0,'M30',NULL),
(34,1,53,1,'M30T',NULL);
/*!40000 ALTER TABLE `manage_device_dictionary` ENABLE KEYS */;
UNLOCK TABLES;
# manage_device_payload
# ------------------------------------------------------------
DROP TABLE IF EXISTS `manage_device_payload`;
CREATE TABLE `manage_device_payload` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`payload_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`payload_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined',
`payload_type` smallint NOT NULL,
`sub_type` smallint NOT NULL,
`version` smallint DEFAULT NULL,
`payload_index` smallint NOT NULL,
`device_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`payload_desc` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`create_time` bigint NOT NULL,
`update_time` bigint NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `payload_sn_UNIQUE` (`payload_sn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
# manage_user
# ------------------------------------------------------------
DROP TABLE IF EXISTS `manage_user`;
CREATE TABLE `manage_user` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`user_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`password` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`workspace_id` int NOT NULL,
`user_type` smallint NOT NULL,
`mqtt_username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`mqtt_password` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`create_time` bigint NOT NULL,
`update_time` bigint NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id_UNIQUE` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
LOCK TABLES `manage_user` WRITE;
/*!40000 ALTER TABLE `manage_user` DISABLE KEYS */;
INSERT INTO `manage_user` (`id`, `user_id`, `username`, `password`, `workspace_id`, `user_type`, `mqtt_username`, `mqtt_password`, `create_time`, `update_time`)
VALUES
(1,'a1559e7c-8dd8-4780-b952-100cc4797da2','adminPC','adminPC',1,1,'admin','admin',1634898410751,1634898410751),
(2,'be7c6c3d-afe9-4be4-b9eb-c55066c0914e','pilot','pilot123',1,2,'pilot','pilot123',1634898410751,1634898410751);
/*!40000 ALTER TABLE `manage_user` ENABLE KEYS */;
UNLOCK TABLES;
# manage_workspace
# ------------------------------------------------------------
DROP TABLE IF EXISTS `manage_workspace`;
CREATE TABLE `manage_workspace` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`workspace_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`workspace_desc` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`platform_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`create_time` bigint NOT NULL,
`update_time` bigint NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `workspace_id_UNIQUE` (`workspace_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
LOCK TABLES `manage_workspace` WRITE;
/*!40000 ALTER TABLE `manage_workspace` DISABLE KEYS */;
INSERT INTO `manage_workspace` (`id`, `workspace_id`, `workspace_name`, `workspace_desc`, `platform_name`, `create_time`, `update_time`)
VALUES
(1,'e3dea0f5-37f2-4d79-ae58-490af3228069','Test Group One','Cloud Sample Test Platform','Cloud Api Platform',1634898410751,1634898410751);
/*!40000 ALTER TABLE `manage_workspace` ENABLE KEYS */;
UNLOCK TABLES;
# map_element_coordinate
# ------------------------------------------------------------
DROP TABLE IF EXISTS `map_element_coordinate`;
CREATE TABLE `map_element_coordinate` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`element_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`longitude` decimal(18,14) NOT NULL,
`latitude` decimal(17,14) NOT NULL,
`altitude` decimal(17,14) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
# map_group
# ------------------------------------------------------------
DROP TABLE IF EXISTS `map_group`;
CREATE TABLE `map_group` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`group_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`group_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`group_type` int NOT NULL,
`workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`is_distributed` tinyint(1) NOT NULL DEFAULT '1',
`is_lock` tinyint(1) NOT NULL DEFAULT '0',
`create_time` bigint NOT NULL,
`update_time` bigint NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `group_id_UNIQUE` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin;
LOCK TABLES `map_group` WRITE;
/*!40000 ALTER TABLE `map_group` DISABLE KEYS */;
INSERT INTO `map_group` (`id`, `group_id`, `group_name`, `group_type`, `workspace_id`, `is_distributed`, `is_lock`, `create_time`, `update_time`)
VALUES
(1,'e3dea0f5-37f2-4d79-ae58-490af3228060','Pilot Share Layer',2,'e3dea0f5-37f2-4d79-ae58-490af3228069',1,0,1638330077356,1638330077356),
(2,'e3dea0f5-37f2-4d79-ae58-490af3228011','Default Layer',1,'e3dea0f5-37f2-4d79-ae58-490af3228069',1,0,1638330077356,1638330077356);
/*!40000 ALTER TABLE `map_group` ENABLE KEYS */;
UNLOCK TABLES;
# map_group_element
# ------------------------------------------------------------
DROP TABLE IF EXISTS `map_group_element`;
CREATE TABLE `map_group_element` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`element_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`element_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`display` smallint NOT NULL DEFAULT '1',
`group_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`element_type` smallint NOT NULL,
`username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`color` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`clamp_to_ground` tinyint(1) NOT NULL DEFAULT '0',
`create_time` bigint NOT NULL,
`update_time` bigint NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `element_id_UNIQUE` (`element_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin;
# media_file
# ------------------------------------------------------------
DROP TABLE IF EXISTS `media_file`;
CREATE TABLE `media_file` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`file_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`file_path` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`fingerprint` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`tinny_fingerprint` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`object_key` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`sub_file_type` int NOT NULL,
`is_original` tinyint(1) NOT NULL,
`drone` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined',
`payload` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined',
`create_time` bigint NOT NULL,
`update_time` bigint NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `fingerprint_UNIQUE` (`fingerprint`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
# wayline_file
# ------------------------------------------------------------
DROP TABLE IF EXISTS `wayline_file`;
CREATE TABLE `wayline_file` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`wayline_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`drone_model_key` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`payload_model_keys` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`favorited` tinyint(1) NOT NULL DEFAULT '0',
`template_types` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`object_key` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`user_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`create_time` bigint NOT NULL,
`update_time` bigint NOT NULL COMMENT 'required, can not modify.',
PRIMARY KEY (`id`),
UNIQUE KEY `wayline_id_UNIQUE` (`wayline_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

17
src/main/java/com/dji/sample/CloudApiSampleApplication.java

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
package com.dji.sample;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@MapperScan("com.dji.sample.*.dao")
@SpringBootApplication
@EnableScheduling
public class CloudApiSampleApplication {
public static void main(String[] args) {
SpringApplication.run(CloudApiSampleApplication.class, args);
}
}

40
src/main/java/com/dji/sample/common/error/CommonErrorEnum.java

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
package com.dji.sample.common.error;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/25
*/
public enum CommonErrorEnum implements IErrorInfo {
SYSTEM_ERROR(600500, "system error"),
SECRET_INVALID(600100, "secret invalid"),
NO_TOKEN(600101, "accss_token is null"),
TOKEN_EXPIRED(600102, "token is expired"),
TOKEN_INVALID(600103, "token invalid"),
SIGN_INVALID(600104, "sign invalid");
private String msg;
private int code;
CommonErrorEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
@Override
public String getErrorMsg() {
return this.msg;
}
@Override
public Integer getErrorCode() {
return this.code;
}
}

22
src/main/java/com/dji/sample/common/error/IErrorInfo.java

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
package com.dji.sample.common.error;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/25
*/
public interface IErrorInfo {
/**
* Get error message.
* @return error message
*/
String getErrorMsg();
/**
* Get error code.
* @return error code
*/
Integer getErrorCode();
}

78
src/main/java/com/dji/sample/common/error/LiveErrorEnum.java

@ -0,0 +1,78 @@ @@ -0,0 +1,78 @@
package com.dji.sample.common.error;
/**
* Live streaming related error codes. When on-demand via mqtt,
* it can be matched with the error code information replied by the pilot.
* @author sean.zhou
* @version 0.1
* @date 2021/11/25
*/
public enum LiveErrorEnum implements IErrorInfo {
NO_AIRCRAFT(613001, "No aircraft."),
NO_CAMERA(613002, "No camera."),
LIVE_STREAM_ALREADY_STARTED(613003, "The camera has started live streaming."),
FUNCTION_NOT_SUPPORT(613004, "The function is not supported."),
STRATEGY_NOT_SUPPORT(613005, "The strategy is not supported."),
NOT_IN_CAMERA_INTERFACE(613006, "The current app is not in the camera interface."),
NO_FLIGHT_CONTROL(613007, "The remote control has no flight control rights and cannot respond to control commands"),
NO_STREAM_DATA(613008, "The current app has no stream data."),
TOO_FREQUENT(613009, "The operation is too frequent."),
ENABLE_FAILED(613010, "Please check whether the live stream service is normal."),
NO_LIVE_STREAM(613011, "There are no live stream currently."),
SWITCH_NOT_SUPPORT(613012, "There is already another camera in the live stream. It's not support to switch the stream directly."),
URL_TYPE_NOT_SUPPORTED(613013, "This url type is not supported."),
ERROR_PARAMETERS(613014, "The live stream parameters are abnormal or incomplete."),
NO_REPLY(613098, "No live reply received."),
UNKNOWN(613099, "UNKNOWN");
private String msg;
private int code;
LiveErrorEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
@Override
public String getErrorMsg() {
return this.msg;
}
@Override
public Integer getErrorCode() {
return this.code;
}
/**
* Get the corresponding enumeration object based on the error code.
* @param code error code
* @return enumeration object
*/
public static LiveErrorEnum find(int code) {
for (LiveErrorEnum errorEnum : LiveErrorEnum.class.getEnumConstants()) {
if (errorEnum.code == code) {
return errorEnum;
}
}
return UNKNOWN;
}
}

88
src/main/java/com/dji/sample/common/model/CustomClaim.java

@ -0,0 +1,88 @@ @@ -0,0 +1,88 @@
package com.dji.sample.common.model;
import com.auth0.jwt.interfaces.Claim;
import com.fasterxml.jackson.annotation.JsonAlias;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* A custom claim for storing custom information in the token.
* @author sean.zhou
* @date 2021/11/16
* @version 0.1
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
@Slf4j
public class CustomClaim {
/**
* The id of the account.
*/
private String id;
private String username;
@JsonAlias("user_type")
private Integer userType;
@JsonAlias("workspace_id")
private String workspaceId;
/**
* Convert the custom claim data type to the Map type.
* @return map
*/
public ConcurrentHashMap<String, String> convertToMap() {
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>(4);
try {
Field[] declaredFields = this.getClass().getDeclaredFields();
for (Field field : declaredFields) {
JsonAlias annotation = field.getAnnotation(JsonAlias.class);
field.setAccessible(true);
// The value of key is named underscore.
map.put(annotation != null ? annotation.value()[0] : field.getName(),
field.get(this).toString());
}
} catch (IllegalAccessException e) {
log.info("CustomClaim converts failed. {}", this.toString());
e.printStackTrace();
}
return map;
}
/**
* Convert the data in Map into a custom claim object.
* @param claimMap
*/
public CustomClaim (Map<String, Claim> claimMap) {
Field[] declaredFields = this.getClass().getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
JsonAlias annotation = field.getAnnotation(JsonAlias.class);
Claim value = claimMap.get(annotation == null ? field.getName() : annotation.value()[0]);
try {
Class<?> type = field.getType();
if (Integer.class.equals(type)) {
field.set(this, Integer.valueOf(value.asString()));
continue;
}
if (String.class.equals(type)) {
field.set(this, value.asString());
continue;
}
} catch (IllegalAccessException e) {
log.info("Claim parses failed. {}", claimMap.toString());
e.printStackTrace();
}
}
}
}

36
src/main/java/com/dji/sample/common/model/Pagination.java

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
package com.dji.sample.common.model;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Data;
/**
* Used for paging display in the wayline. These field names cannot be changed.
* Because they need to be the same as the pilot.
* @author sean
* @version 0.3
* @date 2021/12/22
*/
@Data
public class Pagination {
/**
* The current page number.
*/
private long page;
/**
* The amount of data displayed per page.
*/
private long pageSize;
/**
* The total amount of all data.
*/
private long total;
public Pagination(Page page) {
this.page = page.getCurrent();
this.pageSize = page.getSize();
this.total = page.getTotal();
}
}

27
src/main/java/com/dji/sample/common/model/PaginationData.java

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
package com.dji.sample.common.model;
import lombok.Data;
import java.util.List;
/**
* The format of the data response when a paginated display is required.
* @author sean
* @version 0.3
* @date 2021/12/22
*/
@Data
public class PaginationData<T> {
/**
* The collection in which the data list is stored.
*/
private List<T> list;
private Pagination pagination;
public PaginationData(List<T> list, Pagination pagination) {
this.list = list;
this.pagination = pagination;
}
}

69
src/main/java/com/dji/sample/common/model/ResponseResult.java

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
package com.dji.sample.common.model;
import com.dji.sample.common.error.IErrorInfo;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.http.HttpStatus;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@JsonInclude
public class ResponseResult<T> {
public static final int CODE_SUCCESS = 0;
public static final String MESSAGE_SUCCESS = "success";
private int code;
private String message;
private T data;
public static <T> ResponseResult<T> success(T data) {
return ResponseResult.<T>builder()
.code(CODE_SUCCESS)
.message(MESSAGE_SUCCESS)
.data(data)
.build();
}
public static ResponseResult success() {
return ResponseResult.builder()
.code(0)
.message(MESSAGE_SUCCESS)
.build();
}
public static ResponseResult error() {
return ResponseResult.builder()
.code(HttpStatus.INTERNAL_SERVER_ERROR.value())
.message(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase())
.build();
}
public static ResponseResult error(String message) {
return ResponseResult.builder()
.code(HttpStatus.INTERNAL_SERVER_ERROR.value())
.message(message)
.build();
}
public static ResponseResult error(int code, String message) {
return ResponseResult.builder()
.code(code)
.message(message)
.build();
}
public static ResponseResult error(IErrorInfo errorInfo) {
return ResponseResult.builder()
.code(errorInfo.getErrorCode())
.message(errorInfo.getErrorMsg())
.build();
}
}

103
src/main/java/com/dji/sample/common/util/JwtUtil.java

@ -0,0 +1,103 @@ @@ -0,0 +1,103 @@
package com.dji.sample.common.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.dji.sample.common.model.CustomClaim;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
@Slf4j
@Component
public class JwtUtil {
private static String issuer;
private static String subject;
private static long age;
private static String secret;
private static Algorithm algorithm;
@Value("${jwt.issuer: DJI}")
private void setIssuer(String issuer) {
JwtUtil.issuer = issuer;
}
@Value("${jwt.subject: CloudApiSample}")
private void setSubject(String subject) {
JwtUtil.subject = subject;
}
@Value("${jwt.age: 86400}")
private void setAge(long age) {
JwtUtil.age = age * 1000;
}
@Value("${jwt.secret: CloudApiSample}")
private void setSecret(String secret) {
JwtUtil.secret = secret;
setAlgorithm();
}
private void setAlgorithm() {
JwtUtil.algorithm = Algorithm.HMAC256(secret);
}
/**
* Create a token based on custom information.
* @param claims custom information
* @return token
*/
public static String createToken(Map<String, String> claims) {
Date now = new Date();
JWTCreator.Builder builder = JWT.create();
// Add custom information to the token's payload segment.
claims.forEach(builder::withClaim);
String token = builder.withIssuer(issuer)
.withSubject(subject)
.withIssuedAt(now)
.withExpiresAt(new Date(now.getTime() + age))
.withNotBefore(now)
.sign(algorithm);
log.debug("token created. " + token);
return token;
}
/**
* Verify that the token is valid.
* @param token
* @return
* @throws TokenExpiredException
*/
public static DecodedJWT verifyToken(String token) {
try {
JWTVerifier verifier = JWT.require(algorithm).build();
return verifier.verify(token);
} catch (Exception e) {
log.error(e.getMessage());
e.printStackTrace();
return null;
}
}
/**
* Parses the custom information in the token into a CustomClaim object.
* @param token
* @return custom claim
*/
public static Optional<CustomClaim> parseToken(String token) {
DecodedJWT jwt = verifyToken(token);
return jwt == null ? Optional.empty() : Optional.of(new CustomClaim(jwt.getClaims()));
}
}

40
src/main/java/com/dji/sample/component/ApplicationBootInitial.java

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
package com.dji.sample.component;
import com.dji.sample.manage.model.DeviceStatusManager;
import com.dji.sample.manage.model.enums.DeviceDomainEnum;
import com.dji.sample.manage.model.param.DeviceQueryParam;
import com.dji.sample.manage.service.IDeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* @author sean.zhou
* @date 2021/11/24
* @version 0.1
*/
@Component
public class ApplicationBootInitial implements CommandLineRunner {
@Autowired
private IDeviceService deviceService;
/**
* Subscribe to the devices that exist in the database when the program starts,
* to prevent the data from being different from the pilot side due to program interruptions.
* @param args
* @throws Exception
*/
@Override
public void run(String... args) throws Exception {
deviceService.getDevicesByParams(DeviceQueryParam.builder().build())
.forEach(device -> {
deviceService.subscribeTopicOnline(device.getDeviceSn());
DeviceStatusManager.STATUS_MANAGER.put(
DeviceDomainEnum.getVal(device.getDomain()) + "/"
+ device.getDeviceSn(), LocalDateTime.now());
});
}
}

60
src/main/java/com/dji/sample/component/AuthInterceptor.java

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
package com.dji.sample.component;
import com.dji.sample.common.error.CommonErrorEnum;
import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.common.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;
@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {
public static final String PARAM_TOKEN = "x-auth-token";
public static final String TOKEN_CLAIM = "customClaim";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI();
log.debug("request uri: {}", uri);
// The options method is passed directly.
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
response.setStatus(HttpStatus.OK.value());
return false;
}
String token = request.getHeader(PARAM_TOKEN);
// Check if the token exists.
if (!StringUtils.hasText(token)) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
log.error(CommonErrorEnum.NO_TOKEN.getErrorMsg());
return false;
}
// Check if the current token is valid.
Optional<CustomClaim> customClaimOpt = JwtUtil.parseToken(token);
if (customClaimOpt.isEmpty()) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
}
// Put the custom data from the token into the request.
request.setAttribute(TOKEN_CLAIM, customClaimOpt.get());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// Delete the custom data in the request after the request ends.
request.removeAttribute(TOKEN_CLAIM);
}
}

35
src/main/java/com/dji/sample/component/CorsFilter.java

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
package com.dji.sample.component;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import static com.dji.sample.component.AuthInterceptor.PARAM_TOKEN;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/22
*/
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader("Access-Control-Allow-Credentials", "true");
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
res.addHeader("Access-Control-Allow-Headers", "Access-Control-Allow-Headers," +
"Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, "+
"Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive," +
" User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma," + PARAM_TOKEN);
if (((HttpServletRequest) request).getMethod().equals("OPTIONS")) {
return;
}
filterChain.doFilter(request, response);
}
}

33
src/main/java/com/dji/sample/component/GlobalExceptionHandler.java

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
package com.dji.sample.component;
import com.dji.sample.common.model.ResponseResult;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author sean
* @version 0.2
* @date 2021/12/1
*/
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
/**
* Please do not return directly like this, there is a risk.
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
public ResponseResult exceptionHandler(Exception e) {
e.printStackTrace();
return ResponseResult.error(e.getLocalizedMessage());
}
@ExceptionHandler(NullPointerException.class)
public ResponseResult nullPointerExceptionHandler(NullPointerException e) {
e.printStackTrace();
return ResponseResult.error("A null object appeared.");
}
}

58
src/main/java/com/dji/sample/component/GlobalScheduleService.java

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
package com.dji.sample.component;
import com.dji.sample.manage.model.enums.DeviceDomainEnum;
import com.dji.sample.manage.service.IDeviceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static com.dji.sample.manage.model.DeviceStatusManager.DEFAULT_ALIVE_SECOND;
import static com.dji.sample.manage.model.DeviceStatusManager.STATUS_MANAGER;
/**
* @author sean.zhou
* @date 2021/11/24
* @version 0.1
*/
@Component
@Slf4j
public class GlobalScheduleService {
@Autowired
private IDeviceService deviceService;
/**
* Check the status of the devices every 30 seconds. It is recommended to use cache.
*/
@Scheduled(fixedRate = 30, timeUnit = TimeUnit.SECONDS)
private void deviceStatusListen() {
for (Map.Entry<String, LocalDateTime> entry : STATUS_MANAGER.entrySet()) {
if (entry.getValue().isAfter(
LocalDateTime.now().minusSeconds(DEFAULT_ALIVE_SECOND))) {
continue;
}
String device = entry.getKey();
int index = device.indexOf("/");
STATUS_MANAGER.remove(device);
int type = Integer.parseInt(device.substring(0, index));
String sn = device.substring(index + 1);
// Determine whether it is a gateway device.
if (DeviceDomainEnum.GATEWAY.getVal() == type) {
deviceService.deviceOffline(sn);
deviceService.unsubscribeTopicOffline(sn);
continue;
}
deviceService.subDeviceOffline(sn);
}
}
}

95
src/main/java/com/dji/sample/component/mqtt/config/InboundMessageRouter.java

@ -0,0 +1,95 @@ @@ -0,0 +1,95 @@
package com.dji.sample.component.mqtt.config;
import com.dji.sample.component.mqtt.model.ChannelName;
import lombok.extern.slf4j.Slf4j;
import org.springframework.integration.annotation.Router;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.integration.router.AbstractMessageRouter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Pattern;
import static com.dji.sample.component.mqtt.model.TopicConst.*;
/**
*
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
@Component
@Slf4j
public class InboundMessageRouter extends AbstractMessageRouter {
@Resource(name = ChannelName.INBOUND)
private MessageChannel inboundChannel;
@Resource(name = ChannelName.INBOUND_STATUS)
private MessageChannel statusChannel;
@Resource(name = ChannelName.INBOUND_STATE)
private MessageChannel stateChannel;
@Resource(name = ChannelName.DEFAULT)
private MessageChannel defaultChannel;
@Resource(name = ChannelName.INBOUND_SERVICE_REPLY)
private MessageChannel serviceReplyChannel;
@Resource(name = ChannelName.INBOUND_OSD)
private MessageChannel osdChannel;
private static final Pattern PATTERN_TOPIC_STATUS =
Pattern.compile("^" + BASIC_PRE + PRODUCT + REGEX_SN + STATUS_SUF + "$");
private static final Pattern PATTERN_TOPIC_STATE =
Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + STATE_SUF + "$");
private static final Pattern PATTERN_TOPIC_SERVICE_REPLY =
Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + SERVICES_SUF + _REPLY_SUF + "$");
private static final Pattern PATTERN_TOPIC_OSD =
Pattern.compile("^" + THING_MODEL_PRE + PRODUCT + REGEX_SN + OSD_SUF);
/**
* All mqtt broker messages will arrive here before distributing them to different channels.
* @param message message from mqtt broker
* @return channel
*/
@Override
@Router(inputChannel = ChannelName.INBOUND)
protected Collection<MessageChannel> determineTargetChannels(Message<?> message) {
MessageHeaders headers = message.getHeaders();
String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString();
byte[] payload = (byte[])message.getPayload();
// osd
if (PATTERN_TOPIC_OSD.matcher(topic).matches()) {
return Collections.singleton(osdChannel);
}
log.debug("received topic :{} \t payload :{}", topic, new String(payload));
// status
if (PATTERN_TOPIC_STATUS.matcher(topic).matches()) {
return Collections.singleton(statusChannel);
}
// state
if (PATTERN_TOPIC_STATE.matcher(topic).matches()) {
return Collections.singleton(stateChannel);
}
// services_reply
if (PATTERN_TOPIC_SERVICE_REPLY.matcher(topic).matches()) {
return Collections.singleton(serviceReplyChannel);
}
return Collections.singleton(defaultChannel);
}
}

63
src/main/java/com/dji/sample/component/mqtt/config/MqttConfiguration.java

@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
package com.dji.sample.component.mqtt.config;
import lombok.Data;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
/**
*
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
@Configuration
@Data
@ConfigurationProperties(prefix = "mqtt")
public class MqttConfiguration {
private String protocol;
private String host;
private Integer port;
private String username;
private String password;
private String clientId;
/**
* The topic to subscribe to immediately when client connects.
*/
private String inboundTopic;
@Bean
public MqttConnectOptions mqttConnectOptions() {
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setServerURIs(new String[]{
new StringBuilder()
.append(protocol.trim())
.append("://")
.append(host.trim())
.append(":")
.append(port)
.toString()});
mqttConnectOptions.setUserName(username);
mqttConnectOptions.setPassword(password.toCharArray());
mqttConnectOptions.setAutomaticReconnect(true);
mqttConnectOptions.setKeepAliveInterval(10);
return mqttConnectOptions;
}
@Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setConnectionOptions(mqttConnectOptions());
return factory;
}
}

69
src/main/java/com/dji/sample/component/mqtt/config/MqttInboundConfiguration.java

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
package com.dji.sample.component.mqtt.config;
import com.dji.sample.component.mqtt.model.ChannelName;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.endpoint.MessageProducerSupport;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import javax.annotation.Resource;
/**
* Client configuration for inbound messages.
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
@Slf4j
@Configuration
@IntegrationComponentScan
public class MqttInboundConfiguration {
@Autowired
private MqttConfiguration mqttConfiguration;
@Autowired
private MqttPahoClientFactory mqttClientFactory;
@Resource(name = ChannelName.INBOUND)
private MessageChannel inboundChannel;
/**
* Clients of inbound message channels.
* @return
*/
@Bean(name = "adapter")
public MessageProducerSupport mqttInbound() {
MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(
mqttConfiguration.getClientId() + "_consumer_" + System.currentTimeMillis(),
mqttClientFactory, mqttConfiguration.getInboundTopic().split(","));
DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter();
// use byte types uniformly
converter.setPayloadAsBytes(true);
adapter.setConverter(converter);
adapter.setQos(1);
adapter.setOutputChannel(inboundChannel);
return adapter;
}
/**
* Define a default channel to handle messages that have no effect.
* @return
*/
@Bean
@ServiceActivator(inputChannel = ChannelName.DEFAULT)
public MessageHandler defaultInboundHandler() {
return message -> {
log.info("The default channel does not handle messages.");
};
}
}

90
src/main/java/com/dji/sample/component/mqtt/config/MqttMessageChannel.java

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
package com.dji.sample.component.mqtt.config;
import com.dji.sample.component.mqtt.model.ChannelName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.ExecutorChannel;
import org.springframework.messaging.MessageChannel;
import java.util.concurrent.Executor;
/**
* Definition classes for all channels
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
@Configuration
public class MqttMessageChannel {
@Autowired
private Executor threadPool;
@Bean(name = ChannelName.INBOUND)
public MessageChannel inboundChannel() {
return new ExecutorChannel(threadPool);
}
@Bean(name = ChannelName.INBOUND_STATUS)
public MessageChannel statusChannel() {
return new DirectChannel();
}
@Bean(name = ChannelName.INBOUND_STATUS_ONLINE)
public MessageChannel statusOnlineChannel() {
return new DirectChannel();
}
@Bean(name = ChannelName.INBOUND_STATUS_OFFLINE)
public MessageChannel statusOffChannel() {
return new DirectChannel();
}
@Bean(name = ChannelName.INBOUND_STATE)
public MessageChannel stateChannel() {
return new DirectChannel();
}
@Bean(name = ChannelName.INBOUND_STATE_BASIC)
public MessageChannel stateBasicChannel() {
return new DirectChannel();
}
@Bean(name = ChannelName.INBOUND_STATE_PAYLOAD)
public MessageChannel statePayloadChannel() {
return new DirectChannel();
}
@Bean(name = ChannelName.INBOUND_SERVICE_REPLY)
public MessageChannel serviceReplyChannel() {
return new DirectChannel();
}
@Bean(name = ChannelName.INBOUND_STATE_CAPACITY)
public MessageChannel stateCapacityChannel() {
return new DirectChannel();
}
@Bean(name = ChannelName.INBOUND_STATE_PAYLOAD_UPDATE)
public MessageChannel statePayloadUpdateChannel() {
return new DirectChannel();
}
@Bean(name = ChannelName.INBOUND_OSD)
public MessageChannel osdChannel() {
return new DirectChannel();
}
@Bean(name = ChannelName.DEFAULT)
public MessageChannel defaultChannel() {
return new DirectChannel();
}
@Bean(name = ChannelName.OUTBOUND)
public MessageChannel outboundChannel() {
return new DirectChannel();
}
}

47
src/main/java/com/dji/sample/component/mqtt/config/MqttOutboundConfiguration.java

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
package com.dji.sample.component.mqtt.config;
import com.dji.sample.component.mqtt.model.ChannelName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.MessageHandler;
/**
* Client configuration for outbound messages.
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
@Configuration
public class MqttOutboundConfiguration {
@Autowired
private MqttConfiguration mqttConfiguration;
@Autowired
private MqttPahoClientFactory mqttClientFactory;
/**
* Clients of outbound message channels.
* @return
*/
@Bean
@ServiceActivator(inputChannel = ChannelName.OUTBOUND)
public MessageHandler mqttOutbound() {
MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(
mqttConfiguration.getClientId() + "_producer_" + System.currentTimeMillis(),
mqttClientFactory);
DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter();
// use byte types uniformly
converter.setPayloadAsBytes(true);
messageHandler.setAsync(true);
messageHandler.setDefaultQos(0);
messageHandler.setConverter(converter);
return messageHandler;
}
}

46
src/main/java/com/dji/sample/component/mqtt/model/ChannelName.java

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
package com.dji.sample.component.mqtt.model;
/**
* The name of all channels.
*
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
public class ChannelName {
public static final String INBOUND = "inbound";
public static final String INBOUND_STATUS = "inboundStatus";
public static final String INBOUND_STATUS_ROUTER = "inboundStatusRouter";
public static final String INBOUND_STATUS_ONLINE = "inboundStatusOnline";
public static final String INBOUND_STATUS_OFFLINE = "inboundStatusOffline";
public static final String INBOUND_STATE = "inboundState";
public static final String INBOUND_STATE_SPLITTER = "inboundStateSplitter";
public static final String INBOUND_STATE_ROUTER = "inboundStateRouter";
public static final String INBOUND_STATE_BASIC = "inboundStateBasic";
public static final String INBOUND_STATE_PAYLOAD = "inboundStatePayload";
public static final String INBOUND_STATE_PAYLOAD_UPDATE = "inboundStatePayloadUpdate";
public static final String INBOUND_STATE_CAPACITY = "inboundStateCapacity";
public static final String INBOUND_STATE_LIST = "inboundStateList";
public static final String INBOUND_SERVICE_REPLY = "inboundStateServiceReply";
public static final String INBOUND_OSD = "inboundOsd";
public static final String DEFAULT = "default";
public static final String OUTBOUND = "outbound";
}

29
src/main/java/com/dji/sample/component/mqtt/model/CommonTopicReceiver.java

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
package com.dji.sample.component.mqtt.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
/**
* Unified topic receiving format.
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class CommonTopicReceiver<T> {
/**
* The command is sent and the response is matched by the tid and bid fields in the message,
* and the reply should keep the tid and bid the same.
*/
private String tid;
private String bid;
private String method;
private Long timestamp;
private T data;
}

33
src/main/java/com/dji/sample/component/mqtt/model/CommonTopicResponse.java

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
package com.dji.sample.component.mqtt.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Unified Topic response format
* @author sean.zhou
* @date 2021/11/15
* @version 0.1
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@JsonIgnoreProperties(ignoreUnknown = true)
public class CommonTopicResponse<T> {
/**
* The command is sent and the response is matched by the tid and bid fields in the message,
* and the reply should keep the tid and bid the same.
*/
private String tid;
private String bid;
private String method;
private T data;
}

29
src/main/java/com/dji/sample/component/mqtt/model/TopicConst.java

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
package com.dji.sample.component.mqtt.model;
/**
* All the topics that need to be used in the project.
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
public class TopicConst {
public static final String BASIC_PRE = "sys/";
public static final String THING_MODEL_PRE = "thing/";
public static final String PRODUCT = "product/";
public static final String STATUS_SUF = "/status";
public static final String _REPLY_SUF = "_reply";
public static final String STATE_SUF = "/state";
public static final String SERVICES_SUF = "/services";
public static final String OSD_SUF = "/osd";
public static final String REGEX_SN = "[A-Za-z0-9]+";
}

29
src/main/java/com/dji/sample/component/mqtt/model/TopicStateReceiver.java

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
package com.dji.sample.component.mqtt.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
/**
* The data format of the state topic.
* @author sean.zhou
* @date 2021/11/17
* @version 0.1
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class TopicStateReceiver<T> {
private String tid;
private String bid;
private Long timestamp;
/**
* The sn of the gateway device.
*/
private String gateway;
private T data;
}

27
src/main/java/com/dji/sample/component/mqtt/service/IMessageSenderService.java

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
package com.dji.sample.component.mqtt.service;
import com.dji.sample.component.mqtt.model.CommonTopicResponse;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/25
*/
public interface IMessageSenderService {
/**
* Publish a message to a specific topic.
* @param topic target
* @param response message
*/
void publish(String topic, CommonTopicResponse response);
/**
* Use a specific qos to push messages to a specific topic.
* @param topic target
* @param qos qos
* @param response message
*/
void publish(String topic, int qos, CommonTopicResponse response);
}

33
src/main/java/com/dji/sample/component/mqtt/service/IMqttMessageGateway.java

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
package com.dji.sample.component.mqtt.service;
import com.dji.sample.component.mqtt.model.ChannelName;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
/**
*
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
@Component
@MessagingGateway(defaultRequestChannel = ChannelName.OUTBOUND)
public interface IMqttMessageGateway {
/**
* Publish a message to a specific topic.
* @param topic target
* @param payload message
*/
void publish(@Header(MqttHeaders.TOPIC) String topic, byte[] payload);
/**
* Use a specific qos to push messages to a specific topic.
* @param topic target
* @param payload message
* @param qos qos
*/
void publish(@Header(MqttHeaders.TOPIC) String topic, byte[] payload, @Header(MqttHeaders.QOS) int qos);
}

38
src/main/java/com/dji/sample/component/mqtt/service/IMqttTopicService.java

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
package com.dji.sample.component.mqtt.service;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.handler.annotation.Header;
/**
*
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
public interface IMqttTopicService {
/**
* Subscribe to a specific topic.
* @param topic target
*/
void subscribe(@Header(MqttHeaders.TOPIC) String topic);
/**
* Subscribe to a specific topic using a specific qos.
* @param topic target
* @param qos qos
*/
void subscribe(@Header(MqttHeaders.TOPIC) String topic, int qos);
/**
* Unsubscribe from a specific topic.
* @param topic target
*/
void unsubscribe(@Header(MqttHeaders.TOPIC) String topic);
/**
* Get all the subscribed topics.
* @return topics
*/
String[] getSubscribedTopic();
}

50
src/main/java/com/dji/sample/component/mqtt/service/impl/MessageSenderServiceImpl.java

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
package com.dji.sample.component.mqtt.service.impl;
import com.dji.sample.component.mqtt.model.CommonTopicResponse;
import com.dji.sample.component.mqtt.service.IMessageSenderService;
import com.dji.sample.component.mqtt.service.IMqttMessageGateway;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author sean.zhou
* @date 2021/11/16
* @version 0.1
*/
@Service
@Slf4j
public class MessageSenderServiceImpl implements IMessageSenderService {
@Autowired
private IMqttMessageGateway messageGateway;
public void publish(String topic, CommonTopicResponse response) {
try {
ObjectMapper mapper = new ObjectMapper();
// Only parameters whose value is not null will be serialised.
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
messageGateway.publish(topic, mapper.writeValueAsBytes(response));
} catch (JsonProcessingException e) {
log.info("Failed to publish the message. {}", response.toString());
e.printStackTrace();
}
}
public void publish(String topic, int qos, CommonTopicResponse response) {
try {
ObjectMapper mapper = new ObjectMapper();
// Only parameters whose value is not null will be serialised.
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
messageGateway.publish(topic, mapper.writeValueAsBytes(response), qos);
} catch (JsonProcessingException e) {
log.info("Failed to publish the message. {}", response.toString());
e.printStackTrace();
}
}
}

44
src/main/java/com/dji/sample/component/mqtt/service/impl/MqttTopicServiceImpl.java

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
package com.dji.sample.component.mqtt.service.impl;
import com.dji.sample.component.mqtt.service.IMqttTopicService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
*
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
@Component
@Slf4j
public class MqttTopicServiceImpl implements IMqttTopicService {
@Resource
private MqttPahoMessageDrivenChannelAdapter adapter;
@Override
public void subscribe(String topic) {
log.debug("subscribe topic: {}", topic);
adapter.addTopic(topic);
}
@Override
public void subscribe(String topic, int qos) {
log.debug("subscribe topic: {}", topic);
adapter.addTopic(topic, qos);
}
@Override
public void unsubscribe(String topic) {
log.debug("unsubscribe topic: {}", topic);
adapter.removeTopic(topic);
}
public String[] getSubscribedTopic() {
return adapter.getTopic();
}
}

24
src/main/java/com/dji/sample/component/mybatis/MybatisPlusConfiguration.java

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
package com.dji.sample.component.mybatis;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author sean
* @version 0.3
* @date 2021/12/22
*/
@Configuration
public class MybatisPlusConfiguration {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// select database
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}

37
src/main/java/com/dji/sample/component/mybatis/MybatisPlusMetaObjectHandler.java

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
package com.dji.sample.component.mybatis;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
/**
* Automatic filling for set values
*/
@Component
public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
/**
* Automatic filling when inserting into the database.
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Long.class,
LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
this.strictInsertFill(metaObject, "updateTime", Long.class,
LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
}
/**
* Automatic filling when updating the data.
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", Long.class,
LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
}
}

104
src/main/java/com/dji/sample/component/oss/model/AliyunOSSConfiguration.java

@ -0,0 +1,104 @@ @@ -0,0 +1,104 @@
package com.dji.sample.component.oss.model;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
* @author sean
* @version 0.2
* @date 2021/12/9
*/
@Configuration
public class AliyunOSSConfiguration {
/**
* default
*/
public static final String PROVIDER = "ali";
/**
* Whether to use the current storage service.
*/
public static boolean enable;
/**
* The protocol needs to be included at the beginning of the address.
*/
public static String endpoint;
public static String accessKey;
public static String secretKey;
public static String region;
public static Long expire;
public static String roleSessionName;
public static String roleArn;
public static String bucket;
public static String objectDirPrefix;
@Value("${aliyun.oss.endpoint}")
private void setEndpoint(String endpoint) {
AliyunOSSConfiguration.endpoint = endpoint;
}
@Value("${aliyun.oss.access-key}")
private void setAccessKey(String accessKey) {
AliyunOSSConfiguration.accessKey = accessKey;
}
@Value("${aliyun.oss.secret-key}")
private void setSecretKey(String secretKey) {
AliyunOSSConfiguration.secretKey = secretKey;
}
@Value("${aliyun.oss.region}")
private void setRegion(String region) {
AliyunOSSConfiguration.region = region;
}
@Value("${aliyun.oss.expire: 3600}")
private void setExpire(Long expire) {
AliyunOSSConfiguration.expire = expire;
}
@Value("${aliyun.oss.enable: false}")
private void setEnable(boolean enable) {
AliyunOSSConfiguration.enable = enable;
}
@Value("${aliyun.oss.role-session-name}")
private void setRoleSessionName(String roleSessionName) {
AliyunOSSConfiguration.roleSessionName = roleSessionName;
}
@Value("${aliyun.oss.role-arn}")
private void setRoleArn(String roleArn) {
AliyunOSSConfiguration.roleArn = roleArn;
}
@Value("${aliyun.oss.bucket}")
private void setBucket(String bucket) {
AliyunOSSConfiguration.bucket = bucket;
}
@Value("${aliyun.oss.object-dir-prefix: wayline}")
private void setObjectDir(String objectDirPrefix) {
AliyunOSSConfiguration.objectDirPrefix = objectDirPrefix;
}
@Bean
@Lazy
public OSS ossClient() {
return new OSSClientBuilder().build(endpoint, accessKey, secretKey);
}
}

93
src/main/java/com/dji/sample/component/oss/model/MinIOConfiguration.java

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
package com.dji.sample.component.oss.model;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.util.StringUtils;
/**
* @author sean
* @version 0.2
* @date 2021/12/7
*/
@Configuration
public class MinIOConfiguration {
/**
* default
*/
public static final String PROVIDER = "aws";
/**
* Whether to use the current storage service.
*/
public static boolean enable;
public static String endpoint;
public static String accessKey;
public static String secretKey;
public static String region;
public static String bucket;
public static Integer expire;
public static String objectDirPrefix;
@Value("${minio.endpoint: http://localhost:9000/}")
private void setEndpoint(String endpoint) {
MinIOConfiguration.endpoint = endpoint;
}
@Value("${minio.access-key: minioadmin}")
private void setAccessKey(String accessKey) {
MinIOConfiguration.accessKey = accessKey;
}
@Value("${minio.secret-key: minioadmin}")
private void setSecretKey(String secretKey) {
MinIOConfiguration.secretKey = secretKey;
}
@Value("${minio.region: }")
private void setRegion(String region) {
MinIOConfiguration.region = region;
}
@Value("${minio.bucket: test}")
private void setBucket(String bucket) {
MinIOConfiguration.bucket = bucket;
}
@Value("${minio.expire: 3600}")
private void setExpire(Integer expire) {
MinIOConfiguration.expire = expire;
}
@Value("${minio.enable: false}")
private void setEnable(boolean enable) {
MinIOConfiguration.enable = enable;
}
@Value("${minio.object-dir-prefix: wayline}")
private void setObjectDir(String objectDirPrefix) {
MinIOConfiguration.objectDirPrefix = objectDirPrefix;
}
@Bean
@Lazy
public MinioClient minioClient() {
MinioClient.Builder builder = MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey);
if (StringUtils.hasText(region)) {
builder.region(MinIOConfiguration.region);
}
return builder.build();
}
}

27
src/main/java/com/dji/sample/component/oss/service/IOssService.java

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
package com.dji.sample.component.oss.service;
import com.dji.sample.media.model.CredentialsDTO;
import java.net.URL;
/**
* @author sean
* @version 0.3
* @date 2021/12/23
*/
public interface IOssService {
/**
* Get temporary credentials.
* @return
*/
CredentialsDTO getCredentials();
/**
* Get the address of the object based on the bucket name and the object name.
* @param bucket bucket name
* @param objectKey object name
* @return download link
*/
URL getObjectUrl(String bucket, String objectKey);
}

68
src/main/java/com/dji/sample/component/oss/service/impl/AliyunOssServiceImpl.java

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
package com.dji.sample.component.oss.service.impl;
import com.aliyun.oss.OSS;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import com.dji.sample.component.oss.model.AliyunOSSConfiguration;
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.net.URL;
import java.util.Date;
/**
* @author sean
* @version 0.3
* @date 2021/12/23
*/
@Service
@Slf4j
public class AliyunOssServiceImpl implements IOssService {
@Autowired
private OSS ossClient;
@Override
public CredentialsDTO getCredentials() {
try {
DefaultProfile profile = DefaultProfile.getProfile(
AliyunOSSConfiguration.region, AliyunOSSConfiguration.accessKey, AliyunOSSConfiguration.secretKey);
IAcsClient client = new DefaultAcsClient(profile);
AssumeRoleRequest request = new AssumeRoleRequest();
request.setDurationSeconds(AliyunOSSConfiguration.expire);
request.setRoleArn(AliyunOSSConfiguration.roleArn);
request.setRoleSessionName(AliyunOSSConfiguration.roleSessionName);
AssumeRoleResponse response = client.getAcsResponse(request);
return new CredentialsDTO(response.getCredentials(), AliyunOSSConfiguration.expire);
} catch (ClientException e) {
log.debug("Failed to obtain sts.");
e.printStackTrace();
}
return null;
}
@Override
public URL getObjectUrl(String bucket, String objectKey) {
if (!StringUtils.hasText(bucket) || !StringUtils.hasText(objectKey)) {
return null;
}
// First check if the object can be fetched.
ossClient.getObject(bucket, objectKey);
return ossClient.generatePresignedUrl(bucket, objectKey,
new Date(System.currentTimeMillis() + AliyunOSSConfiguration.expire * 1000));
}
}

66
src/main/java/com/dji/sample/component/oss/service/impl/MinIOServiceImpl.java

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
package com.dji.sample.component.oss.service.impl;
import com.dji.sample.component.oss.model.MinIOConfiguration;
import com.dji.sample.component.oss.service.IOssService;
import com.dji.sample.media.model.CredentialsDTO;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MinioClient;
import io.minio.credentials.AssumeRoleProvider;
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.IOException;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
* @author sean
* @version 0.3
* @date 2021/12/23
*/
@Service
@Slf4j
public class MinIOServiceImpl implements IOssService {
@Autowired
private MinioClient client;
@Override
public CredentialsDTO getCredentials() {
try {
AssumeRoleProvider provider = new AssumeRoleProvider(MinIOConfiguration.endpoint, MinIOConfiguration.accessKey,
MinIOConfiguration.secretKey, MinIOConfiguration.expire,
null, null, null, null, null, null);
return new CredentialsDTO(provider.fetch(), MinIOConfiguration.expire);
} catch (NoSuchAlgorithmException e) {
log.debug("Failed to obtain sts.");
e.printStackTrace();
}
return null;
}
@Override
public URL getObjectUrl(String bucket, String objectKey) {
try {
return new URL(
client.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucket)
.object(objectKey)
.expiry(MinIOConfiguration.expire)
.build()));
} catch (ErrorResponseException | InsufficientDataException | InternalException |
InvalidKeyException | InvalidResponseException | IOException |
NoSuchAlgorithmException | XmlParserException | ServerException e) {
log.error("The file does not exist on the oss.");
e.printStackTrace();
}
return null;
}
}

70
src/main/java/com/dji/sample/component/websocket/config/AuthPrincipalHandler.java

@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
package com.dji.sample.component.websocket.config;
import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.common.util.JwtUtil;
import com.dji.sample.component.AuthInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.Map;
import java.util.Optional;
/**
* @author sean.zhou
* @date 2021/11/16
* @version 0.1
*/
@Slf4j
@Component
public class AuthPrincipalHandler extends DefaultHandshakeHandler {
@Override
protected boolean isValidOrigin(ServerHttpRequest request) {
if (request instanceof ServletServerHttpRequest) {
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
String token = servletRequest.getParameter(AuthInterceptor.PARAM_TOKEN);
if (!StringUtils.hasText(token)) {
return false;
}
Optional<CustomClaim> customClaim = JwtUtil.parseToken(token);
if (customClaim.isEmpty()) {
return false;
}
servletRequest.setAttribute(AuthInterceptor.TOKEN_CLAIM, customClaim.get());
return true;
}
return false;
}
/**
* The principal's name: {workspaceId}/{userType}/{userId}
* @param request
* @param wsHandler
* @param attributes
* @return
*/
@Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
if (request instanceof ServletServerHttpRequest) {
// get the custom claim
CustomClaim claim = (CustomClaim) ((ServletServerHttpRequest) request).getServletRequest()
.getAttribute(AuthInterceptor.TOKEN_CLAIM);
return () -> claim.getWorkspaceId() + "/" + claim.getUserType() + "/" + claim.getId();
}
return () -> null;
}
}

25
src/main/java/com/dji/sample/component/websocket/config/ConcurrentWebSocketSession.java

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
package com.dji.sample.component.websocket.config;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/24
*/
public class ConcurrentWebSocketSession extends ConcurrentWebSocketSessionDecorator {
private static final int SEND_BUFFER_SIZE_LIMIT = 1024 * 1024;
private static final int SEND_TIME_LIMIT = 1000;
private ConcurrentWebSocketSession(WebSocketSession delegate, int sendTimeLimit, int bufferSizeLimit) {
super(delegate, sendTimeLimit, bufferSizeLimit);
}
ConcurrentWebSocketSession(WebSocketSession delegate) {
this(delegate, SEND_TIME_LIMIT, SEND_BUFFER_SIZE_LIMIT);
}
}

20
src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultFactory.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
package com.dji.sample.component.websocket.config;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
/**
*
* @author sean.zhou
* @date 2021/11/16
* @version 0.1
*/
@Component
public class WebSocketDefaultFactory implements WebSocketHandlerDecoratorFactory {
@Override
public WebSocketHandler decorate(WebSocketHandler handler) {
return new WebSocketDefaultHandler(handler);
}
}

60
src/main/java/com/dji/sample/component/websocket/config/WebSocketDefaultHandler.java

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
package com.dji.sample.component.websocket.config;
import com.dji.sample.component.websocket.model.WebSocketManager;
import com.dji.sample.component.websocket.service.ISendMessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
import java.security.Principal;
/**
*
* @author sean.zhou
* @date 2021/11/16
* @version 0.1
*/
@Slf4j
public class WebSocketDefaultHandler extends WebSocketHandlerDecorator {
@Autowired
private ISendMessageService sendMessageService;
WebSocketDefaultHandler(WebSocketHandler delegate) {
super(delegate);
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
Principal principal = session.getPrincipal();
if (StringUtils.hasText(principal.getName())) {
WebSocketManager.put(principal.getName(), new ConcurrentWebSocketSession(session));
log.debug("{} is connected. ID: {}. WebSocketSession[current count: {}]",
principal.getName(), session.getId(), WebSocketManager.getConnectedCount());
return;
}
session.close();
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
Principal principal = session.getPrincipal();
if (StringUtils.hasText(principal.getName())) {
WebSocketManager.remove(principal.getName(), session.getId());
log.debug("{} is disconnected. ID: {}. WebSocketSession[current count: {}]",
principal.getName(), session.getId(), WebSocketManager.getConnectedCount());
}
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
log.debug("received message: {}", message.getPayload());
}
}

39
src/main/java/com/dji/sample/component/websocket/config/WebSocketMessageConfiguration.java

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
package com.dji.sample.component.websocket.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
/**
*
* @author sean.zhou
* @date 2021/11/17
* @version 0.1
*/
@EnableWebSocketMessageBroker
@Configuration
public class WebSocketMessageConfiguration implements WebSocketMessageBrokerConfigurer {
@Autowired
private AuthPrincipalHandler authPrincipalHandler;
@Autowired
private WebSocketDefaultFactory webSocketDefaultFactory;
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// Set the WebSocket connection address
registry.addEndpoint("/api/v1/ws").setAllowedOriginPatterns("*")
.setHandshakeHandler(authPrincipalHandler);
}
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
registry.addDecoratorFactory(webSocketDefaultFactory);
registry.setTimeToFirstMessage(60000 * 60 * 24 * 10);
}
}

37
src/main/java/com/dji/sample/component/websocket/model/BizCodeEnum.java

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
package com.dji.sample.component.websocket.model;
/**
* @author sean
* @version 0.1
* @date 2021/11/26
*/
public enum BizCodeEnum {
DEVICE_ONLINE("device_online"),
DEVICE_OFFLINE("device_offline"),
DEVICE_UPDATE_TOPO("device_update_topo"),
DEVICE_OSD("device_osd"),
GATEWAY_OSD("gateway_osd"),
MAP_ELEMENT_CREATE("map_element_create"),
MAP_ELEMENT_UPDATE("map_element_update"),
MAP_ELEMENT_DELETE("map_element_delete"),
MAP_GROUP_REFRESH("map_group_refresh");
private String code;
BizCodeEnum(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}

30
src/main/java/com/dji/sample/component/websocket/model/CustomWebSocketMessage.java

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
package com.dji.sample.component.websocket.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.Data;
/**
* The format of WebSocket messages that the pilot can receive.
* @author sean.zhou
* @date 2021/11/17
* @version 0.1
*/
@Data
@Builder
public class CustomWebSocketMessage<T> {
/**
* @see BizCodeEnum
* specific value
*/
@JsonProperty("biz_code")
private String bizCode;
@Builder.Default
private String version = "1.0";
private Long timestamp;
private T data;
}

112
src/main/java/com/dji/sample/component/websocket/model/WebSocketManager.java

@ -0,0 +1,112 @@ @@ -0,0 +1,112 @@
package com.dji.sample.component.websocket.model;
import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession;
import com.dji.sample.manage.model.enums.UserTypeEnum;
import lombok.extern.slf4j.Slf4j;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Manage all WebSocket connection objects.
* @author sean.zhou
* @date 2021/11/16
* @version 0.1
*/
@Slf4j
public class WebSocketManager {
private static final ConcurrentHashMap<String,
ConcurrentHashMap<String,
ConcurrentHashMap<String, ConcurrentWebSocketSession>>> MANAGER = new ConcurrentHashMap<>(16);
/**
* WebSocket connection from the pilot.
*/
private static final Set<ConcurrentWebSocketSession> PILOT_SESSION = ConcurrentHashMap.newKeySet(16);
/**
* WebSocket connection from the web.
*/
private static final Set<ConcurrentWebSocketSession> WEB_SESSION = ConcurrentHashMap.newKeySet(16);
public static void put(String key, ConcurrentWebSocketSession val) {
String[] name = key.split("/");
if (name.length != 3) {
log.debug("The key is out of format. [{workspaceId}/{userType}/{userId}]");
return;
}
ConcurrentHashMap<String, ConcurrentHashMap<String, ConcurrentWebSocketSession>> workspaceSessions =
MANAGER.getOrDefault(name[0], new ConcurrentHashMap<>(16));
ConcurrentHashMap<String, ConcurrentWebSocketSession> userSessions = workspaceSessions.getOrDefault(
name[2], new ConcurrentHashMap<>(16));
userSessions.put(val.getId(), val);
workspaceSessions.put(name[2], userSessions);
MANAGER.put(name[0], workspaceSessions);
getSetByUserType(Integer.valueOf(name[1])).add(val);
}
public static void remove(String key, String sessionId) {
String[] name = key.split("/");
if (name.length != 3) {
log.debug("The key is out of format. [{workspaceId}/{userType}/{userId}]");
return;
}
ConcurrentHashMap<String, ConcurrentWebSocketSession> userSession = MANAGER.get(name[0]).get(name[2]);
Set<ConcurrentWebSocketSession> typeSession = getSetByUserType(Integer.valueOf(name[1]));
ConcurrentWebSocketSession session = userSession.get(sessionId);
typeSession.remove(session);
userSession.remove(sessionId);
}
public static int getConnectedCount() {
return PILOT_SESSION.size() + WEB_SESSION.size();
}
public static Collection<ConcurrentWebSocketSession> getValueWithWorkspace(String workspaceId) {
Set<ConcurrentWebSocketSession> sessions = ConcurrentHashMap.newKeySet();
MANAGER.get(workspaceId)
.forEach((userId, userSessions) -> {
sessions.addAll(userSessions.values());
});
return sessions;
}
public static Collection<ConcurrentWebSocketSession> getValueWithWorkspaceAndUserType(String workspaceId, Integer userType) {
Set<ConcurrentWebSocketSession> sessions = ConcurrentHashMap.newKeySet();
Set<ConcurrentWebSocketSession> typeSessions = getSetByUserType(userType);
MANAGER.getOrDefault(workspaceId, new ConcurrentHashMap<>())
.forEach((userId, userSessions) -> {
Collection<ConcurrentWebSocketSession> sessionList = userSessions.values();
if (!sessionList.isEmpty()) {
ConcurrentWebSocketSession session = sessionList.iterator().next();
if (typeSessions.contains(session)) {
sessions.addAll(sessionList);
}
}
});
return sessions;
}
private static Set<ConcurrentWebSocketSession> getSetByUserType(Integer userType) {
if (UserTypeEnum.PILOT.getVal() == userType) {
return PILOT_SESSION;
}
if (UserTypeEnum.WEB.getVal() == userType) {
return WEB_SESSION;
}
return new HashSet<>();
}
}

28
src/main/java/com/dji/sample/component/websocket/service/ISendMessageService.java

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
package com.dji.sample.component.websocket.service;
import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession;
import com.dji.sample.component.websocket.model.CustomWebSocketMessage;
import java.util.Collection;
/**
* @author sean.zhou
* @date 2021/11/24
* @version 0.1
*/
public interface ISendMessageService {
/**
* Send a message to the specific connection.
* @param session A WebSocket connection object
* @param message message
*/
void sendMessage(ConcurrentWebSocketSession session, CustomWebSocketMessage message);
/**
* Send the same message to specific connection.
* @param sessions A collection of WebSocket connection objects.
* @param message message
*/
void sendBatch(Collection<ConcurrentWebSocketSession> sessions, CustomWebSocketMessage message);
}

72
src/main/java/com/dji/sample/component/websocket/service/impl/SendMessageServiceImpl.java

@ -0,0 +1,72 @@ @@ -0,0 +1,72 @@
package com.dji.sample.component.websocket.service.impl;
import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession;
import com.dji.sample.component.websocket.model.CustomWebSocketMessage;
import com.dji.sample.component.websocket.service.ISendMessageService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import java.io.IOException;
import java.util.Collection;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/24
*/
@Service
@Slf4j
public class SendMessageServiceImpl implements ISendMessageService {
@Override
public void sendMessage(ConcurrentWebSocketSession session, CustomWebSocketMessage message) {
if (session == null) {
return;
}
try {
if (!session.isOpen()) {
session.close();
log.debug("This session is closed.");
return;
}
ObjectMapper mapper = new ObjectMapper();
session.sendMessage(new TextMessage(mapper.writeValueAsBytes(message)));
} catch (IOException e) {
log.info("Failed to publish the message. {}", message.toString());
e.printStackTrace();
}
}
@Override
public void sendBatch(Collection<ConcurrentWebSocketSession> sessions, CustomWebSocketMessage message) {
if (sessions.isEmpty()) {
return;
}
try {
ObjectMapper mapper = new ObjectMapper();
TextMessage data = new TextMessage(mapper.writeValueAsBytes(message));
for (ConcurrentWebSocketSession session : sessions) {
if (!session.isOpen()) {
session.close();
log.debug("This session is closed.");
return;
}
session.sendMessage(data);
}
} catch (IOException e) {
log.info("Failed to publish the message. {}", message.toString());
e.printStackTrace();
}
}
}

35
src/main/java/com/dji/sample/configuration/GlobalMVCConfigurer.java

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
package com.dji.sample.configuration;
import com.dji.sample.component.AuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class GlobalMVCConfigurer implements WebMvcConfigurer {
@Autowired
private AuthInterceptor authInterceptor;
private static List<String> excludePaths = new ArrayList<>();
@Value("${url.manage.prefix}")
private String managePrefix;
@Value("${url.manage.version}")
private String manageVersion;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// Exclude the login interface.
excludePaths.add(managePrefix + manageVersion + "/login");
excludePaths.add(managePrefix + manageVersion + "/token/refresh");
// Intercept for all request interfaces.
registry.addInterceptor(authInterceptor).addPathPatterns("/**").excludePathPatterns(excludePaths);
}
}

42
src/main/java/com/dji/sample/configuration/GlobalThreadPoolConfiguration.java

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
package com.dji.sample.configuration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.*;
/**
*
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
@Configuration
public class GlobalThreadPoolConfiguration {
@Value("${thread.pool.core-pool-size: 10}")
private int corePoolSize;
@Value("${thread.pool.maximum-pool-size: 20}")
private int maximumPoolSize;
@Value("${thread.pool.keep-alive-time: 60}")
private long keepAliveTime;
@Value("${thread.pool.queue.capacity: 1000}")
private int capacity;
/**
* A custom thread pool.
* @return
*/
@Bean
public Executor threadPool() {
return new ThreadPoolExecutor(corePoolSize,
maximumPoolSize, keepAliveTime,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(capacity),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());
}
}

138
src/main/java/com/dji/sample/manage/controller/DeviceController.java

@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
package com.dji.sample.manage.controller;
import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.component.AuthInterceptor;
import com.dji.sample.component.mqtt.model.ChannelName;
import com.dji.sample.component.mqtt.model.CommonTopicReceiver;
import com.dji.sample.component.mqtt.model.CommonTopicResponse;
import com.dji.sample.component.websocket.model.BizCodeEnum;
import com.dji.sample.component.websocket.model.CustomWebSocketMessage;
import com.dji.sample.component.websocket.model.WebSocketManager;
import com.dji.sample.component.websocket.service.ISendMessageService;
import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.dto.WorkspaceDTO;
import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.dji.sample.manage.model.param.DeviceQueryParam;
import com.dji.sample.manage.model.receiver.StatusGatewayReceiver;
import com.dji.sample.manage.service.IDeviceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.Message;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/15
*/
@RestController
@Slf4j
@RequestMapping("${url.manage.prefix}${url.manage.version}/devices")
public class DeviceController {
@Autowired
private IDeviceService deviceService;
@Autowired
private ISendMessageService sendMessageService;
/**
* Handles the message that the drone goes online.
* @param receiver The drone information is not empty.
*/
@ServiceActivator(inputChannel = ChannelName.INBOUND_STATUS_ONLINE, outputChannel = ChannelName.OUTBOUND)
public void deviceOnline(CommonTopicReceiver<StatusGatewayReceiver> receiver) {
boolean online = deviceService.deviceOnline(receiver.getData());
if (online) {
// Notify pilot that the drone is online successfully.
deviceService.publishStatusReply(receiver.getData().getSn(),
CommonTopicResponse.builder()
.tid(receiver.getTid())
.bid(receiver.getBid())
.build());
// Publish the latest device topology information in the current workspace to the pilot.
deviceService.pushDeviceOnlineTopo(WorkspaceDTO.DEFAULT_WORKSPACE_ID,
receiver.getData().getSn(), receiver.getData().getSubDevices().get(0).getSn());
}
}
/**
* Handles the message that the drone goes offline.
* @param receiver The drone information is empty.
*/
@ServiceActivator(inputChannel = ChannelName.INBOUND_STATUS_OFFLINE, outputChannel = ChannelName.OUTBOUND)
public void deviceOffline(CommonTopicReceiver<StatusGatewayReceiver> receiver) {
boolean offline = deviceService.deviceOffline(receiver.getData().getSn());
if (offline) {
// Notify pilot that the device is offline successfully.
deviceService.publishStatusReply(receiver.getData().getSn(),
CommonTopicResponse.builder()
.tid(receiver.getTid())
.bid(receiver.getBid())
.build());
// Publish the latest device topology information in the current workspace to the pilot.
deviceService.pushDeviceOfflineTopo(WorkspaceDTO.DEFAULT_WORKSPACE_ID, receiver.getData().getSn());
}
}
/**
* Get the topology list of all devices in the current user workspace.
* @param request
* @return
*/
@GetMapping("/devices")
public ResponseResult<List<DeviceDTO>> getDevices(HttpServletRequest request) {
// Get information about the current user.
CustomClaim claim = (CustomClaim)request.getAttribute(AuthInterceptor.TOKEN_CLAIM);
String workspaceId = claim.getWorkspaceId();
// Get information about the devices in the current user's workspace.
List<DeviceDTO> devicesList = deviceService.getDevicesTopoForWeb(workspaceId);
return ResponseResult.success(devicesList);
}
@ServiceActivator(inputChannel = ChannelName.INBOUND_OSD)
public void osdRealTime(Message<?> message) {
String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString();
byte[] payload = (byte[])message.getPayload();
deviceService.handleOSD(topic, payload);
}
/**
* Handles the payloads data of the drone.
* @param deviceSn drone's sn
*/
@ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_PAYLOAD_UPDATE)
public void pushWebSocketDevices(String deviceSn) {
List<DeviceDTO> devicesList = deviceService.getDevicesByParams(
DeviceQueryParam.builder()
.deviceSn(deviceSn)
.build());
// Get drone information based on the sn of the drone. The sn of the drone is unique.
DeviceDTO device = devicesList.get(0);
// Set the remote controller and payloads information of the drone.
deviceService.spliceDeviceTopo(device);
CustomWebSocketMessage wsMessage = CustomWebSocketMessage.builder()
.timestamp(System.currentTimeMillis())
.bizCode(BizCodeEnum.DEVICE_UPDATE_TOPO.getCode())
.data(device)
.build();
// Update the topology of the drone via WebSocket notifications to the web side.
sendMessageService.sendBatch(WebSocketManager
.getValueWithWorkspaceAndUserType(
device.getWorkspaceId(), UserTypeEnum.WEB.getVal()),
wsMessage);
}
}

66
src/main/java/com/dji/sample/manage/controller/DevicePayloadController.java

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
package com.dji.sample.manage.controller;
import com.dji.sample.component.mqtt.model.ChannelName;
import com.dji.sample.manage.model.receiver.DeviceBasicReceiver;
import com.dji.sample.manage.model.receiver.DevicePayloadReceiver;
import com.dji.sample.manage.service.IDevicePayloadService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author sean.zhou
* @date 2021/11/19
* @version 0.1
*/
@RestController
@Slf4j
public class DevicePayloadController {
@Autowired
private IDevicePayloadService devicePayloadService;
/**
* Handles the data for the payload messages in the state topic.
* @param payloadsList List of payload information.
* @return drone's sn
*/
@ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_PAYLOAD,
outputChannel = ChannelName.INBOUND_STATE_PAYLOAD_UPDATE)
public String statePayload(List<DevicePayloadReceiver> payloadsList) {
// Delete all payload information for the drone based on the drone's sn.
devicePayloadService.deletePayloadsByDeviceSn(List.of(payloadsList.get(0).getDeviceSn()));
// Save the new payload information.
devicePayloadService.savePayloadDTOs(payloadsList);
log.debug("The result of saving the payload is successful.");
return payloadsList.get(0).getDeviceSn();
}
/**
* Handles messages in the state topic about basic drone data.
*
* Note: Only the data of the drone payload is handled here. You can handle other data from the drone
* according to your business needs.
* @param deviceBasic basic drone data
* @return drone's sn
*/
@ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_BASIC,
outputChannel = ChannelName.INBOUND_STATE_PAYLOAD_UPDATE)
public String stateBasic(DeviceBasicReceiver deviceBasic) {
// Delete all payload information for the drone based on the drone's sn.
devicePayloadService.deletePayloadsByDeviceSn(List.of(deviceBasic.getDeviceSn()));
// Save the new payload information.
boolean isSave = devicePayloadService.savePayloadDTOs(deviceBasic.getPayloads());
log.debug("The result of saving the payloads is {}.", isSave);
return deviceBasic.getDeviceSn();
}
}

113
src/main/java/com/dji/sample/manage/controller/LiveStreamController.java

@ -0,0 +1,113 @@ @@ -0,0 +1,113 @@
package com.dji.sample.manage.controller;
import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.component.mqtt.model.ChannelName;
import com.dji.sample.component.mqtt.model.CommonTopicReceiver;
import com.dji.sample.manage.model.Chan;
import com.dji.sample.manage.model.dto.CapacityDeviceDTO;
import com.dji.sample.manage.model.dto.LiveTypeDTO;
import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver;
import com.dji.sample.manage.model.receiver.ServiceReplyReceiver;
import com.dji.sample.manage.service.ILiveStreamService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.messaging.Message;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/19
*/
@RestController
@Slf4j
@RequestMapping("${url.manage.prefix}${url.manage.version}/live")
public class LiveStreamController {
@Autowired
private ILiveStreamService liveStreamService;
/**
* Analyze the live streaming capabilities of drones.
* This data is necessary if drones are required for live streaming.
* @param device the capacity of drone
*/
@ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_CAPACITY)
public void stateCapacity(CapacityDeviceReceiver device) {
boolean parseCapacity = liveStreamService.saveLiveCapacity(device);
log.debug("The result of parsing the live capacity is {}.", parseCapacity);
}
/**
* Get live capability data of all drones in the current user's workspace from the database.
* @param request
* @return live capability
*/
@GetMapping("/capacity")
public ResponseResult<List<CapacityDeviceDTO>> getLiveCapacity(HttpServletRequest request) {
// Get information about the current user.
CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
List<CapacityDeviceDTO> liveCapacity = liveStreamService.getLiveCapacity(customClaim.getWorkspaceId());
return ResponseResult.success(liveCapacity);
}
/**
* Live streaming according to the parameters passed in from the web side.
* @param liveParam Live streaming parameters.
* @return
*/
@PostMapping("/streams/start")
public ResponseResult liveStart(@RequestBody LiveTypeDTO liveParam) {
return liveStreamService.liveStart(liveParam);
}
/**
* Stop live streaming according to the parameters passed in from the web side.
* @param liveParam Live streaming parameters.
* @return
*/
@PostMapping("/streams/stop")
public ResponseResult liveStop(@RequestBody LiveTypeDTO liveParam) {
return liveStreamService.liveStop(liveParam.getVideoId());
}
/**
* Set the quality of the live streaming according to the parameters passed in from the web side.
* @param liveParam Live streaming parameters.
* @return
*/
@PostMapping("/streams/update")
public ResponseResult liveSetQuality(@RequestBody LiveTypeDTO liveParam) {
return liveStreamService.liveSetQuality(liveParam);
}
/**
* Handle the reply message from the pilot side to the on-demand video.
* @param message reply message
* @throws IOException
*/
@ServiceActivator(inputChannel = ChannelName.INBOUND_SERVICE_REPLY)
public void serviceReply(Message<?> message) throws IOException {
byte[] payload = (byte[])message.getPayload();
ObjectMapper mapper = new ObjectMapper();
CommonTopicReceiver<ServiceReplyReceiver> receiver = mapper.readValue(payload,
new TypeReference<CommonTopicReceiver<ServiceReplyReceiver>>() {
});
Chan<CommonTopicReceiver> chan = Chan.getInstance();
// Put the message to the chan object.
chan.put(receiver);
}
}

47
src/main/java/com/dji/sample/manage/controller/LoginController.java

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
package com.dji.sample.manage.controller;
import com.dji.sample.common.error.CommonErrorEnum;
import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.manage.model.dto.UserDTO;
import com.dji.sample.manage.model.dto.UserLoginDTO;
import com.dji.sample.manage.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;
import static com.dji.sample.component.AuthInterceptor.PARAM_TOKEN;
@RestController
@RequestMapping("${url.manage.prefix}${url.manage.version}")
public class LoginController {
@Autowired
private IUserService userService;
@PostMapping("/login")
public ResponseResult login(@RequestBody UserLoginDTO loginDTO) {
String username = loginDTO.getUsername();
String password = loginDTO.getPassword();
return userService.userLogin(username, password);
}
@PostMapping("/token/refresh")
public ResponseResult refreshToken(HttpServletRequest request, HttpServletResponse response) {
String token = request.getHeader(PARAM_TOKEN);
Optional<UserDTO> user = userService.refreshToken(token);
if (user.isEmpty()) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return ResponseResult.error(CommonErrorEnum.NO_TOKEN.getErrorMsg());
}
return ResponseResult.success(user);
}
}

40
src/main/java/com/dji/sample/manage/controller/TopologyController.java

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
package com.dji.sample.manage.controller;
import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.manage.model.dto.TopologyDTO;
import com.dji.sample.manage.service.ITopologyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author sean
* @version 0.2
* @date 2021/12/8
*/
@RestController
@RequestMapping("${url.manage.prefix}${url.manage.version}/workspaces")
public class TopologyController {
@Autowired
private ITopologyService topologyService;
/**
* Get the topology list of all devices in the current user workspace for pilot display.
* @param workspaceId
* @return
*/
@GetMapping("/{workspace_id}/devices/topologies")
public ResponseResult<Map<String, List<TopologyDTO>>> getDevicesTopologiesForPilot(
@PathVariable(name = "workspace_id") String workspaceId) {
List<TopologyDTO> topologyList = topologyService.getDeviceTopology(workspaceId);
return ResponseResult.success(new ConcurrentHashMap<>(Map.of("list", topologyList)));
}
}

29
src/main/java/com/dji/sample/manage/controller/UserController.java

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
package com.dji.sample.manage.controller;
import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.manage.service.IUserService;
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.RestController;
import javax.servlet.http.HttpServletRequest;
import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;
@RestController
@RequestMapping("${url.manage.prefix}${url.manage.version}/users")
public class UserController {
@Autowired
private IUserService userService;
@GetMapping("/current")
public ResponseResult getCurrentUserInfo(HttpServletRequest request) {
CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
return userService.getUserByUsername(customClaim.getUsername(), customClaim.getWorkspaceId());
}
}

41
src/main/java/com/dji/sample/manage/controller/WorkspaceController.java

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
package com.dji.sample.manage.controller;
import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.manage.model.dto.WorkspaceDTO;
import com.dji.sample.manage.service.IWorkspaceService;
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.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Optional;
import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/23
*/
@RestController
@RequestMapping("${url.manage.prefix}${url.manage.version}/workspaces")
public class WorkspaceController {
@Autowired
private IWorkspaceService workspaceService;
/**
* Gets information about the workspace that the current user is in.
* @param request
* @return
*/
@GetMapping("/current")
public ResponseResult getCurrentWorkspace(HttpServletRequest request) {
CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
Optional<WorkspaceDTO> workspaceOpt = workspaceService.getWorkspaceByWorkspaceId(customClaim.getWorkspaceId());
return workspaceOpt.isEmpty() ? ResponseResult.error() : ResponseResult.success(workspaceOpt.get());
}
}

12
src/main/java/com/dji/sample/manage/dao/ICameraVideoMapper.java

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
package com.dji.sample.manage.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dji.sample.manage.model.entity.CameraVideoEntity;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/19
*/
public interface ICameraVideoMapper extends BaseMapper<CameraVideoEntity> {
}

12
src/main/java/com/dji/sample/manage/dao/ICapacityCameraMapper.java

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
package com.dji.sample.manage.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dji.sample.manage.model.entity.CapacityCameraEntity;
/**
* @author sean.zhou
* @date 2021/11/19
* @version 0.1
*/
public interface ICapacityCameraMapper extends BaseMapper<CapacityCameraEntity> {
}

13
src/main/java/com/dji/sample/manage/dao/IDeviceDictionaryMapper.java

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
package com.dji.sample.manage.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dji.sample.manage.model.entity.DeviceDictionaryEntity;
/**
*
* @author sean.zhou
* @date 2021/11/15
* @version 0.1
*/
public interface IDeviceDictionaryMapper extends BaseMapper<DeviceDictionaryEntity> {
}

14
src/main/java/com/dji/sample/manage/dao/IDeviceMapper.java

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
package com.dji.sample.manage.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dji.sample.manage.model.entity.DeviceEntity;
/**
*
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
public interface IDeviceMapper extends BaseMapper<DeviceEntity> {
}

12
src/main/java/com/dji/sample/manage/dao/IDevicePayloadMapper.java

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
package com.dji.sample.manage.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dji.sample.manage.model.entity.DevicePayloadEntity;
/**
* @author sean.zhou
* @date 2021/11/19
* @version 0.1
*/
public interface IDevicePayloadMapper extends BaseMapper<DevicePayloadEntity> {
}

8
src/main/java/com/dji/sample/manage/dao/IUserMapper.java

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
package com.dji.sample.manage.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dji.sample.manage.model.entity.UserEntity;
public interface IUserMapper extends BaseMapper<UserEntity> {
}

8
src/main/java/com/dji/sample/manage/dao/IWorkspaceMapper.java

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
package com.dji.sample.manage.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dji.sample.manage.model.entity.WorkspaceEntity;
public interface IWorkspaceMapper extends BaseMapper<WorkspaceEntity> {
}

35
src/main/java/com/dji/sample/manage/handler/AbstractStateTopicHandler.java

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
package com.dji.sample.manage.handler;
import com.dji.sample.component.mqtt.model.TopicStateReceiver;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
/**
* @author sean
* @version 0.3
* @date 2022/2/21
*/
public abstract class AbstractStateTopicHandler {
protected AbstractStateTopicHandler handler;
protected static ObjectMapper mapper = new ObjectMapper();;
protected AbstractStateTopicHandler(AbstractStateTopicHandler handler){
this.handler = handler;
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
/**
* Passing dataNode data, using different processing methods depending on the data selection.
* @param dataNode
* @param stateReceiver
* @param sn
* @return
* @throws JsonProcessingException
*/
public abstract TopicStateReceiver handleState(JsonNode dataNode, TopicStateReceiver stateReceiver, String sn) throws JsonProcessingException;
}

25
src/main/java/com/dji/sample/manage/handler/StateDefaultHandler.java

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
package com.dji.sample.manage.handler;
import com.dji.sample.component.mqtt.model.TopicStateReceiver;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.stereotype.Service;
/**
* @author sean
* @version 0.3
* @date 2022/3/21
*/
@Service
public class StateDefaultHandler extends AbstractStateTopicHandler {
protected StateDefaultHandler() {
super(null);
}
@Override
public TopicStateReceiver handleState(JsonNode dataNode, TopicStateReceiver stateReceiver, String sn) throws JsonProcessingException {
// If no suitable handler is found for the data, it is not processed.
return stateReceiver;
}
}

34
src/main/java/com/dji/sample/manage/handler/StateDeviceBasicHandler.java

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
package com.dji.sample.manage.handler;
import com.dji.sample.component.mqtt.model.TopicStateReceiver;
import com.dji.sample.manage.model.receiver.DeviceBasicReceiver;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
* @author sean
* @version 0.3
* @date 2022/2/21
*/
@Service
public class StateDeviceBasicHandler extends AbstractStateTopicHandler {
public StateDeviceBasicHandler(@Autowired @Qualifier("statePayloadHandler") AbstractStateTopicHandler handler) {
super(handler);
}
@Override
public TopicStateReceiver handleState(JsonNode dataNode, TopicStateReceiver stateReceiver, String sn) throws JsonProcessingException {
// handle device basic data
if (dataNode.size() != 1) {
DeviceBasicReceiver data = mapper.treeToValue(dataNode, DeviceBasicReceiver.class);
data.setDeviceSn(sn);
stateReceiver.setData(data);
return stateReceiver;
}
return handler.handleState(dataNode, stateReceiver, sn);
}
}

39
src/main/java/com/dji/sample/manage/handler/StateLiveCapacityHandler.java

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
package com.dji.sample.manage.handler;
import com.dji.sample.component.mqtt.model.TopicStateReceiver;
import com.dji.sample.manage.model.enums.StateDataEnum;
import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
* @author sean
* @version 0.3
* @date 2022/2/21
*/
@Service
public class StateLiveCapacityHandler extends AbstractStateTopicHandler {
private static final String DEVICE_LIST = "device_list";
protected StateLiveCapacityHandler(@Autowired @Qualifier("stateDefaultHandler") AbstractStateTopicHandler handler) {
super(handler);
}
@Override
public TopicStateReceiver handleState(JsonNode dataNode, TopicStateReceiver stateReceiver, String sn) throws JsonProcessingException {
String name = dataNode.fieldNames().next();
JsonNode childNode = dataNode.findPath(name);
// Determine if it is live capacity data based on name.
if (name.equals(StateDataEnum.LIVE_CAPACITY.getDesc())) {
JsonNode deviceNode = childNode.findPath(DEVICE_LIST);
stateReceiver.setData(
mapper.treeToValue(deviceNode.get(0), CapacityDeviceReceiver.class));
return stateReceiver;
}
return handler.handleState(dataNode, stateReceiver, sn);
}
}

55
src/main/java/com/dji/sample/manage/handler/StatePayloadHandler.java

@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
package com.dji.sample.manage.handler;
import com.dji.sample.component.mqtt.model.TopicStateReceiver;
import com.dji.sample.manage.model.enums.StateDataEnum;
import com.dji.sample.manage.model.receiver.DevicePayloadReceiver;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author sean
* @version 0.3
* @date 2022/2/21
*/
@Service
public class StatePayloadHandler extends AbstractStateTopicHandler {
protected StatePayloadHandler(@Autowired @Qualifier("stateLiveCapacityHandler") AbstractStateTopicHandler handler) {
super(handler);
}
@Override
public TopicStateReceiver handleState(JsonNode dataNode, TopicStateReceiver stateReceiver, String sn) throws JsonProcessingException {
String name = dataNode.fieldNames().next();
JsonNode childNode = dataNode.findPath(name);
// Determine if it is payload data based on name.
if (name.equals(StateDataEnum.PAYLOADS.getDesc())) {
List<DevicePayloadReceiver> payloadsList = new ArrayList<>();
Iterator<JsonNode> payloadsNode = childNode.elements();
while (payloadsNode.hasNext()) {
DevicePayloadReceiver payloadReceiver = mapper.treeToValue(
payloadsNode.next(), DevicePayloadReceiver.class);
payloadReceiver.setDeviceSn(sn);
payloadsList.add(payloadReceiver);
}
if (payloadsList.isEmpty()) {
DevicePayloadReceiver payloadReceiver = new DevicePayloadReceiver();
payloadReceiver.setDeviceSn(sn);
payloadsList.add(payloadReceiver);
}
stateReceiver.setData(payloadsList);
return stateReceiver;
}
return handler.handleState(dataNode, stateReceiver, sn);
}
}

92
src/main/java/com/dji/sample/manage/handler/StateRouter.java

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
package com.dji.sample.manage.handler;
import com.dji.sample.component.mqtt.model.ChannelName;
import com.dji.sample.component.mqtt.model.TopicStateReceiver;
import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver;
import com.dji.sample.manage.model.receiver.DeviceBasicReceiver;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Router;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.integration.router.MessageRouter;
import org.springframework.integration.router.PayloadTypeRouter;
import org.springframework.messaging.Message;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import static com.dji.sample.component.mqtt.model.TopicConst.*;
/**
*
* @author sean.zhou
* @date 2021/11/17
* @version 0.1
*/
@MessageEndpoint
@Slf4j
@Configuration
public class StateRouter {
@Resource(name = "stateDeviceBasicHandler")
private AbstractStateTopicHandler handler;
/**
* Handles the routing of state topic messages. Depending on the data, it is assigned to different channels for handling.
* @param message
* @return
* @throws IOException
*/
@ServiceActivator(inputChannel = ChannelName.INBOUND_STATE, outputChannel = ChannelName.INBOUND_STATE_SPLITTER)
public TopicStateReceiver<?> resolveStateData(Message<?> message) throws IOException {
byte[] payload = (byte[])message.getPayload();
String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString();
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
TopicStateReceiver stateReceiver = mapper.readValue(payload, TopicStateReceiver.class);
// Get the sn of the topic source.
String from = topic.substring((THING_MODEL_PRE + PRODUCT).length(),
topic.indexOf(STATE_SUF));
try {
JsonNode dataNode = mapper.readTree(payload).findPath("data");
return handler.handleState(dataNode, stateReceiver, from);
} catch (UnrecognizedPropertyException e) {
log.info("The {} data is not processed.", e.getPropertyName());
}
return stateReceiver;
}
@Bean
@Router(inputChannel = ChannelName.INBOUND_STATE_ROUTER)
public MessageRouter resolveStateRouter() {
PayloadTypeRouter router = new PayloadTypeRouter();
// // Channel mapping for basic data.
router.setChannelMapping(DeviceBasicReceiver.class.getName(),
ChannelName.INBOUND_STATE_BASIC);
// Channel mapping for live streaming capabilities.
router.setChannelMapping(CapacityDeviceReceiver.class.getName(),
ChannelName.INBOUND_STATE_CAPACITY);
// Channel mapping for payload data.
router.setChannelMapping(List.class.getName(),
ChannelName.INBOUND_STATE_PAYLOAD);
router.setChannelMapping(Map.class.getName(),
ChannelName.DEFAULT);
return router;
}
}

59
src/main/java/com/dji/sample/manage/handler/StateSplitter.java

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
package com.dji.sample.manage.handler;
import com.dji.sample.component.mqtt.model.ChannelName;
import com.dji.sample.component.mqtt.model.TopicStateReceiver;
import com.dji.sample.manage.model.receiver.DevicePayloadReceiver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Splitter;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import java.util.ArrayList;
import java.util.Collection;
/**
*
* @author sean.zhou
* @date 2021/11/17
* @version 0.1
*/
@MessageEndpoint
@Configuration
public class StateSplitter {
/**
* Split the state message data to different channels for handling according to their different types.
* @param receiver state message
* @return
*/
@Splitter(inputChannel = ChannelName.INBOUND_STATE_SPLITTER, outputChannel = ChannelName.INBOUND_STATE_ROUTER)
public Collection<Object> splitState(TopicStateReceiver receiver) {
ArrayList<Object> type = new ArrayList<>();
type.add(receiver.getData());
return type;
}
/**
* Split according to the different types in the list.
* @return
*/
@Bean
public IntegrationFlow splitList() {
return IntegrationFlows
.from(ChannelName.INBOUND_STATE_LIST)
.split()
.<Object, String> route(dataType -> {
Class<?> clazz = dataType.getClass();
if (DevicePayloadReceiver.class.isAssignableFrom(clazz)) {
return ChannelName.INBOUND_STATE_PAYLOAD;
}
return null;
}, mapping -> {
mapping.channelMapping(ChannelName.INBOUND_STATE_PAYLOAD,
ChannelName.INBOUND_STATE_PAYLOAD);
})
.get();
}
}

70
src/main/java/com/dji/sample/manage/handler/StatusRouter.java

@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
package com.dji.sample.manage.handler;
import com.dji.sample.component.mqtt.model.ChannelName;
import com.dji.sample.component.mqtt.model.CommonTopicReceiver;
import com.dji.sample.manage.model.enums.DeviceDomainEnum;
import com.dji.sample.manage.model.receiver.StatusGatewayReceiver;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Router;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.Message;
import org.springframework.util.CollectionUtils;
import static com.dji.sample.component.mqtt.model.TopicConst.*;
/**
*
* @author sean.zhou
* @date 2021/11/12
* @version 0.1
*/
@MessageEndpoint
public class StatusRouter {
/**
* Converts the status data sent by the gateway device into an object.
* @param message
* @return
*/
@ServiceActivator(inputChannel = ChannelName.INBOUND_STATUS, outputChannel = ChannelName.INBOUND_STATUS_ROUTER)
public CommonTopicReceiver<StatusGatewayReceiver> resolveStatus(Message<?> message) {
CommonTopicReceiver<StatusGatewayReceiver> statusReceiver = new CommonTopicReceiver<>();
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
try {
statusReceiver = mapper.readValue(
(byte[])message.getPayload(),
new TypeReference<CommonTopicReceiver<StatusGatewayReceiver>>() {});
String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString();
// set gateway's domain
statusReceiver.getData().setDomain(DeviceDomainEnum.GATEWAY.getVal());
// set gateway's sn
statusReceiver.getData().setSn(
topic.substring((BASIC_PRE + PRODUCT).length(),
topic.indexOf(STATUS_SUF)));
} catch (Exception e) {
e.printStackTrace();
}
return statusReceiver;
}
/**
* Handles the routing of status topic messages. Depending on the data, it is assigned to different channels for handling.
* @param receiver
* @return
*/
@Router(inputChannel = ChannelName.INBOUND_STATUS_ROUTER)
public String resolveStatusRouter(CommonTopicReceiver<StatusGatewayReceiver> receiver) {
// Determine whether the drone is online or offline according to whether the data of the sub-device is empty.
return CollectionUtils.isEmpty(receiver.getData().getSubDevices()) ?
ChannelName.INBOUND_STATUS_OFFLINE : ChannelName.INBOUND_STATUS_ONLINE;
}
}

45
src/main/java/com/dji/sample/manage/model/Chan.java

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
package com.dji.sample.manage.model;
import java.util.concurrent.locks.LockSupport;
/**
* The demo is only for functional closure, which is not recommended.
* @author sean.zhou
* @date 2021/11/22
* @version 0.1
*/
public class Chan<T> {
private static final long THREAD_WAIT_TIME = 1000_000 * 500;
private volatile T data;
private volatile Thread t;
private Chan () {
}
public static Chan getInstance() {
return ChanSingleton.INSTANCE;
}
public T get(Object blocker) {
this.t = Thread.currentThread();
LockSupport.parkNanos(blocker, THREAD_WAIT_TIME);
this.t = null;
return data;
}
public void put(T data) {
this.data = data;
if (t == null) {
return;
}
LockSupport.unpark(t);
}
private static class ChanSingleton {
private static final Chan<?> INSTANCE = new Chan<>();
}
}

20
src/main/java/com/dji/sample/manage/model/DeviceStatusManager.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
package com.dji.sample.manage.model;
import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;
/**
* The demo is only for functional closure, which is not recommended,
* and it is recommended to use caching for handling.
* @author sean.zhou
* @version 0.1
* @date 2021/11/25
*/
public class DeviceStatusManager {
public static final ConcurrentHashMap<String, LocalDateTime> STATUS_MANAGER =
new ConcurrentHashMap<>(16);
public static final Integer DEFAULT_ALIVE_SECOND = 30;
}

34
src/main/java/com/dji/sample/manage/model/dto/CapacityCameraDTO.java

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
package com.dji.sample.manage.model.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author sean.zhou
* @date 2021/11/22
* @version 0.1
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CapacityCameraDTO {
private Integer id;
private String deviceSn;
private String name;
private String description;
private String index;
private String type;
private List<CapacityVideoDTO> videosList;
}

26
src/main/java/com/dji/sample/manage/model/dto/CapacityDeviceDTO.java

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
package com.dji.sample.manage.model.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author sean.zhou
* @date 2021/11/22
* @version 0.1
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CapacityDeviceDTO {
private String sn;
private String name;
private List<CapacityCameraDTO> camerasList;
}

24
src/main/java/com/dji/sample/manage/model/dto/CapacityVideoDTO.java

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
package com.dji.sample.manage.model.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author sean.zhou
* @date 2021/11/22
* @version 0.1
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CapacityVideoDTO {
private Integer id;
private String index;
private String type;
}

47
src/main/java/com/dji/sample/manage/model/dto/DeviceDTO.java

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
package com.dji.sample.manage.model.dto;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author sean.zhou
* @date 2021/11/19
* @version 0.1
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class DeviceDTO {
private String deviceSn;
private String deviceName;
private String workspaceId;
private String deviceIndex;
private String deviceDesc;
private String childDeviceSn;
private String domain;
private Integer type;
private Integer subType;
private List<DeviceDTO> gatewaysList;
private List<DevicePayloadDTO> payloadsList;
private IconUrlDTO iconUrl;
}

28
src/main/java/com/dji/sample/manage/model/dto/DeviceDictionaryDTO.java

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
package com.dji.sample.manage.model.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/23
*/
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class DeviceDictionaryDTO {
private Integer domain;
private Integer deviceType;
private Integer subType;
private String deviceName;
private String deviceDesc;
}

27
src/main/java/com/dji/sample/manage/model/dto/DeviceModelDTO.java

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
package com.dji.sample.manage.model.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author sean
* @version 0.2
* @date 2021/12/8
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class DeviceModelDTO {
private String key;
private String domain;
private String type;
private String subType;
}

29
src/main/java/com/dji/sample/manage/model/dto/DevicePayloadDTO.java

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
package com.dji.sample.manage.model.dto;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author sean.zhou
* @date 2021/11/19
* @version 0.1
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class DevicePayloadDTO {
private String payloadSn;
private String payloadName;
private Integer payloadIndex;
private String payloadDesc;
}

25
src/main/java/com/dji/sample/manage/model/dto/IconUrlDTO.java

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
package com.dji.sample.manage.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author sean
* @version 0.3
* @date 2022/1/5
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class IconUrlDTO {
@JsonProperty("normal_icon_url")
private String normalUrl;
@JsonProperty("selected_icon_url")
private String selectUrl;
}

20
src/main/java/com/dji/sample/manage/model/dto/LiveDTO.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
package com.dji.sample.manage.model.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/23
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class LiveDTO {
private String url;
private String username;
private String password;
}

26
src/main/java/com/dji/sample/manage/model/dto/LiveTypeDTO.java

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
package com.dji.sample.manage.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* Receive live parameters.
* @author sean.zhou
* @version 0.1
* @date 2021/11/22
*/
@Data
public class LiveTypeDTO {
@JsonProperty("url_type")
private Integer urlType;
private String url;
@JsonProperty("video_id")
private String videoId;
@JsonProperty("video_quality")
private Integer videoQuality;
}

20
src/main/java/com/dji/sample/manage/model/dto/LiveUrlAgoraDTO.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
package com.dji.sample.manage.model.dto;
import lombok.Data;
/**
* @author sean.zhou
* @date 2021/11/23
* @version 0.1
*/
@Data
public class LiveUrlAgoraDTO {
private String channel;
private String sn;
private String token;
private Integer uid;
}

27
src/main/java/com/dji/sample/manage/model/dto/LiveUrlGB28181DTO.java

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
package com.dji.sample.manage.model.dto;
import lombok.Data;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/23
*/
@Data
public class LiveUrlGB28181DTO {
private String serverIP;
private Integer serverPort;
private String serverID;
private String agentID;
private String agentPassword;
private Integer localPort;
private String channel;
}

18
src/main/java/com/dji/sample/manage/model/dto/LiveUrlRTSPDTO.java

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
package com.dji.sample.manage.model.dto;
import lombok.Data;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/23
*/
@Data
public class LiveUrlRTSPDTO {
private String userName;
private String password;
private Integer port;
}

22
src/main/java/com/dji/sample/manage/model/dto/TelemetryDTO.java

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
package com.dji.sample.manage.model.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author sean
* @version 0.2
* @date 2021/12/8
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TelemetryDTO {
private TelemetryDeviceDTO host;
private String sn;
}

32
src/main/java/com/dji/sample/manage/model/dto/TelemetryDeviceDTO.java

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
package com.dji.sample.manage.model.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author sean
* @version 0.2
* @date 2021/12/8
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TelemetryDeviceDTO {
private Double latitude;
private Double longitude;
private Double altitude;
private Float attitudeHead;
private Double elevation;
private Float horizontalSpeed;
private Float verticalSpeed;
}

24
src/main/java/com/dji/sample/manage/model/dto/TopologyDTO.java

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
package com.dji.sample.manage.model.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author sean
* @version 0.2
* @date 2021/12/8
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TopologyDTO {
private List<TopologyDeviceDTO> hosts;
private List<TopologyDeviceDTO> parents;
}

35
src/main/java/com/dji/sample/manage/model/dto/TopologyDeviceDTO.java

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
package com.dji.sample.manage.model.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author sean
* @version 0.2
* @date 2021/12/8
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class TopologyDeviceDTO {
private String sn;
private DeviceModelDTO deviceModel;
@Builder.Default
private Boolean onlineStatus = true;
private String deviceCallsign;
private String userId;
private String userCallsign;
private IconUrlDTO iconUrls;
}

37
src/main/java/com/dji/sample/manage/model/dto/UserDTO.java

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
package com.dji.sample.manage.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {
@JsonProperty("user_id")
private String userId;
private String username;
@JsonProperty("workspace_id")
private String workspaceId;
@JsonProperty("user_type")
private Integer userType;
@JsonProperty("mqtt_username")
private String mqttUsername;
@JsonProperty("mqtt_password")
private String mqttPassword;
@JsonProperty("access_token")
private String accessToken;
@JsonProperty("mqtt_addr")
private String mqttAddr;
}

14
src/main/java/com/dji/sample/manage/model/dto/UserLoginDTO.java

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
package com.dji.sample.manage.model.dto;
import lombok.Data;
import lombok.NonNull;
@Data
public class UserLoginDTO {
@NonNull
private String username;
@NonNull
private String password;
}

33
src/main/java/com/dji/sample/manage/model/dto/WorkspaceDTO.java

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
package com.dji.sample.manage.model.dto;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author sean.zhou
* @date 2021/11/22
* @version 0.1
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class WorkspaceDTO {
public static final String DEFAULT_WORKSPACE_ID = "e3dea0f5-37f2-4d79-ae58-490af3228069";
private Integer id;
private String workspaceId;
private String workspaceName;
private String workspaceDesc;
private String platformName;
}

37
src/main/java/com/dji/sample/manage/model/entity/CameraVideoEntity.java

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
package com.dji.sample.manage.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author sean.zhou
* @date 2021/11/19
* @version 0.1
*/
@TableName(value = "manage_camera_video")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CameraVideoEntity implements Serializable {
@TableId(type = IdType.AUTO)
private Integer id;
@TableField(value = "camera_id")
private Integer cameraId;
@TableField(value = "video_index")
private String videoIndex;
@TableField(value = "video_type")
private String videoType;
}

46
src/main/java/com/dji/sample/manage/model/entity/CapacityCameraEntity.java

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
package com.dji.sample.manage.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author sean.zhou
* @date 2021/11/19
* @version 0.1
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "manage_capacity_camera")
public class CapacityCameraEntity implements Serializable {
@TableId(type = IdType.AUTO)
private Integer id;
@TableField(value = "device_sn")
private String deviceSn;
@TableField(value = "name")
private String name;
@TableField(value = "description")
private String description;
@TableField(value = "camera_index")
private String cameraIndex;
@TableField(value = "coexist_video_number_max")
private Integer coexistVideoNumberMax;
@TableField(value = "available_video_number")
private Integer availableVideoNumber;
}

45
src/main/java/com/dji/sample/manage/model/entity/DeviceDictionaryEntity.java

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
package com.dji.sample.manage.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
*
* @author sean.zhou
* @date 2021/11/15
* @version 0.1
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName("manage_device_dictionary")
public class DeviceDictionaryEntity implements Serializable {
@TableId(type = IdType.AUTO)
private Integer id;
@TableField(value = "domain")
private Integer domain;
@TableField(value = "device_type")
private Integer deviceType;
@TableField(value = "sub_type")
private Integer subType;
@TableField(value = "device_name")
private String deviceName;
@TableField(value = "device_desc")
private String deviceDesc;
}

69
src/main/java/com/dji/sample/manage/model/entity/DeviceEntity.java

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
package com.dji.sample.manage.model.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* The entity class of the device
*
* @author sean.zhou
* @version 0.1
* @date 2021/11/10
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "manage_device")
public class DeviceEntity implements Serializable {
@TableId(type = IdType.AUTO)
private Integer id;
@TableField(value = "device_sn")
private String deviceSn;
@TableField(value = "device_name")
private String deviceName;
@TableField(value = "workspace_id")
private String workspaceId;
@TableField(value = "device_type")
private Integer deviceType;
@TableField(value = "sub_type")
private Integer subType;
@TableField(value = "domain")
private Integer domain;
@TableField(value = "version")
private Integer version;
@TableField(value = "device_index")
private String deviceIndex;
@TableField(value = "child_sn")
private String childSn;
@TableField(value = "create_time", fill = FieldFill.INSERT)
private Long createTime;
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private Long updateTime;
@TableField(value = "device_desc")
private String deviceDesc;
@TableField(value = "url_normal")
private String urlNormal;
@TableField(value = "url_select")
private String urlSelect;
}

56
src/main/java/com/dji/sample/manage/model/entity/DevicePayloadEntity.java

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
package com.dji.sample.manage.model.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author sean.zhou
* @date 2021/11/19
* @version 0.1
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "manage_device_payload")
public class DevicePayloadEntity implements Serializable {
@TableId(type = IdType.AUTO)
private Integer id;
@TableField(value = "payload_sn")
private String payloadSn;
@TableField(value = "payload_name")
private String payloadName;
@TableField(value = "payload_type")
private Integer payloadType;
@TableField(value = "sub_type")
private Integer subType;
@TableField(value = "version")
private Integer version;
@TableField(value = "payload_index")
private Integer payloadIndex;
@TableField(value = "device_sn")
private String deviceSn;
@TableField(value = "create_time", fill = FieldFill.INSERT)
private Long createTime;
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private Long updateTime;
@TableField(value = "payload_desc")
private String payloadDesc;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save