Commit f68f0de05d8070b2264ec45e4904c9284a4eb6f2

Authored by alwayssuper
2 parents 8ff09fea a2532013

Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into origin/feature/iot

Showing 33 changed files with 689 additions and 303 deletions
plugins/disabled.txt 0 → 100644
plugins/enabled.txt 0 → 100644
plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar 0 → 100644
No preview for this file type
yudao-module-iot/pom.xml
@@ -10,7 +10,6 @@ @@ -10,7 +10,6 @@
10 <modules> 10 <modules>
11 <module>yudao-module-iot-api</module> 11 <module>yudao-module-iot-api</module>
12 <module>yudao-module-iot-biz</module> 12 <module>yudao-module-iot-biz</module>
13 - <module>yudao-module-iot-plugin-api</module>  
14 <module>yudao-module-iot-plugin</module> 13 <module>yudao-module-iot-plugin</module>
15 </modules> 14 </modules>
16 <modelVersion>4.0.0</modelVersion> 15 <modelVersion>4.0.0</modelVersion>
yudao-module-iot/yudao-module-iot-api/pom.xml
@@ -21,6 +21,17 @@ @@ -21,6 +21,17 @@
21 <groupId>cn.iocoder.boot</groupId> 21 <groupId>cn.iocoder.boot</groupId>
22 <artifactId>yudao-common</artifactId> 22 <artifactId>yudao-common</artifactId>
23 </dependency> 23 </dependency>
  24 + <!-- PF4J -->
  25 + <dependency>
  26 + <groupId>org.pf4j</groupId>
  27 + <artifactId>pf4j-spring</artifactId>
  28 + <exclusions>
  29 + <exclusion>
  30 + <groupId>org.slf4j</groupId>
  31 + <artifactId>slf4j-log4j12</artifactId>
  32 + </exclusion>
  33 + </exclusions>
  34 + </dependency>
24 </dependencies> 35 </dependencies>
25 36
26 </project> 37 </project>
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/DeviceDataApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.api;
  2 +
  3 +/**
  4 + * 设备数据 API
  5 + */
  6 +public interface DeviceDataApi {
  7 +
  8 + /**
  9 + * 保存设备数据
  10 + *
  11 + * @param productKey 产品 key
  12 + * @param deviceName 设备名称
  13 + * @param message 消息
  14 + */
  15 + void saveDeviceData(String productKey, String deviceName, String message);
  16 +
  17 +}
0 \ No newline at end of file 18 \ No newline at end of file
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.api;
  2 +
  3 +import java.util.HashMap;
  4 +import java.util.Map;
  5 +
  6 +/**
  7 + * 服务注册表 - 插架模块使用,无法使用 Spring 注入
  8 + */
  9 +public class ServiceRegistry {
  10 + private static final Map<Class<?>, Object> services = new HashMap<>();
  11 +
  12 + /**
  13 + * 注册服务
  14 + *
  15 + * @param serviceClass 服务类
  16 + * @param serviceImpl 服务实现
  17 + * @param <T> 服务类
  18 + */
  19 + public static <T> void registerService(Class<T> serviceClass, T serviceImpl) {
  20 + services.put(serviceClass, serviceImpl);
  21 + }
  22 +
  23 + /**
  24 + * 获得服务
  25 + *
  26 + * @param serviceClass 服务类
  27 + * @param <T> 服务类
  28 + * @return 服务实现
  29 + */
  30 + @SuppressWarnings("unchecked")
  31 + public static <T> T getService(Class<T> serviceClass) {
  32 + return (T) services.get(serviceClass);
  33 + }
  34 +}
0 \ No newline at end of file 35 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/pom.xml
@@ -81,6 +81,12 @@ @@ -81,6 +81,12 @@
81 <dependency> 81 <dependency>
82 <groupId>org.pf4j</groupId> 82 <groupId>org.pf4j</groupId>
83 <artifactId>pf4j-spring</artifactId> 83 <artifactId>pf4j-spring</artifactId>
  84 + <exclusions>
  85 + <exclusion>
  86 + <groupId>org.slf4j</groupId>
  87 + <artifactId>slf4j-log4j12</artifactId>
  88 + </exclusion>
  89 + </exclusions>
84 </dependency> 90 </dependency>
85 91
86 </dependencies> 92 </dependencies>
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.api.device;
  2 +
  3 +import cn.iocoder.yudao.module.iot.api.DeviceDataApi;
  4 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService;
  5 +import org.springframework.stereotype.Service;
  6 +import org.springframework.validation.annotation.Validated;
  7 +
  8 +import javax.annotation.Resource;
  9 +
  10 +/**
  11 + * 设备数据 API 实现类
  12 + */
  13 +@Service
  14 +@Validated
  15 +public class DeviceDataApiImpl implements DeviceDataApi {
  16 +
  17 + @Resource
  18 + private IotDeviceDataService iotDeviceDataService;
  19 +
  20 + @Override
  21 + public void saveDeviceData(String productKey, String deviceName, String message) {
  22 + iotDeviceDataService.saveDeviceData(productKey, deviceName, message);
  23 + }
  24 +
  25 +}
0 \ No newline at end of file 26 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.api;
0 \ No newline at end of file 2 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java deleted
1 -/*  
2 - * Copyright (C) 2012-present the original author or authors.  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo;  
17 -  
18 -import cn.iocoder.yudao.module.iot.api.Greeting;  
19 -import org.springframework.beans.factory.annotation.Autowired;  
20 -import org.springframework.stereotype.Component;  
21 -  
22 -import java.util.List;  
23 -  
24 -/**  
25 - * 打招呼 测试用例  
26 - */  
27 -@Component  
28 -public class Greetings {  
29 -  
30 - @Autowired  
31 - private List<Greeting> greetings;  
32 -  
33 - public Integer printGreetings() {  
34 - System.out.printf("找到扩展点的 %d 个扩展 '%s'%n", greetings.size(), Greeting.class.getName());  
35 - for (Greeting greeting : greetings) {  
36 - System.out.println(">>> " + greeting.getGreeting());  
37 - }  
38 - return greetings.size();  
39 - }  
40 -  
41 -}  
42 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugininfo;
3 import jakarta.annotation.Resource; 3 import jakarta.annotation.Resource;
4 import org.pf4j.spring.SpringPluginManager; 4 import org.pf4j.spring.SpringPluginManager;
5 import org.springframework.beans.factory.annotation.Value; 5 import org.springframework.beans.factory.annotation.Value;
6 -import org.springframework.context.ApplicationContext;  
7 import org.springframework.http.HttpStatus; 6 import org.springframework.http.HttpStatus;
8 import org.springframework.http.ResponseEntity; 7 import org.springframework.http.ResponseEntity;
9 import org.springframework.web.bind.annotation.*; 8 import org.springframework.web.bind.annotation.*;
@@ -26,11 +25,7 @@ import java.util.stream.Collectors; @@ -26,11 +25,7 @@ import java.util.stream.Collectors;
26 public class PluginController { 25 public class PluginController {
27 26
28 @Resource 27 @Resource
29 - private ApplicationContext applicationContext;  
30 - @Resource  
31 private SpringPluginManager springPluginManager; 28 private SpringPluginManager springPluginManager;
32 - @Resource  
33 - private Greetings greetings;  
34 29
35 @Value("${pf4j.pluginsDir}") 30 @Value("${pf4j.pluginsDir}")
36 private String pluginsDir; 31 private String pluginsDir;
@@ -73,10 +68,8 @@ public class PluginController { @@ -73,10 +68,8 @@ public class PluginController {
73 68
74 return ResponseEntity.ok("插件上传并加载成功"); 69 return ResponseEntity.ok("插件上传并加载成功");
75 } catch (IOException e) { 70 } catch (IOException e) {
76 - e.printStackTrace();  
77 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("上传插件时发生错误: " + e.getMessage()); 71 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("上传插件时发生错误: " + e.getMessage());
78 } catch (Exception e) { 72 } catch (Exception e) {
79 - e.printStackTrace();  
80 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("加载插件时发生错误: " + e.getMessage()); 73 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("加载插件时发生错误: " + e.getMessage());
81 } 74 }
82 } 75 }
@@ -120,15 +113,4 @@ public class PluginController { @@ -120,15 +113,4 @@ public class PluginController {
120 return ResponseEntity.ok(plugins); 113 return ResponseEntity.ok(plugins);
121 } 114 }
122 115
123 - /**  
124 - * 打印问候语  
125 - *  
126 - * @return 问候语数量  
127 - */  
128 - @PermitAll  
129 - @GetMapping("/printGreetings")  
130 - public ResponseEntity<Integer> printGreetings() {  
131 - Integer count = greetings.printGreetings();  
132 - return ResponseEntity.ok(count);  
133 - }  
134 -} 116 +}
135 \ No newline at end of file 117 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/WhazzupGreeting.java deleted
1 -/*  
2 - * Copyright (C) 2012-present the original author or authors.  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo;  
17 -  
18 -import cn.iocoder.yudao.module.iot.api.Greeting;  
19 -import org.pf4j.Extension;  
20 -import org.springframework.stereotype.Component;  
21 -  
22 -/**  
23 - * 打招呼 测试用例  
24 - */  
25 -@Extension  
26 -@Component  
27 -public class WhazzupGreeting implements Greeting {  
28 -  
29 - @Override  
30 - public String getGreeting() {  
31 - return "Whazzup";  
32 - }  
33 -  
34 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java
@@ -2,8 +2,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo; @@ -2,8 +2,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo;
2 2
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.*; 4 import lombok.*;
5 -import java.util.*;  
6 -import org.springframework.format.annotation.DateTimeFormat;  
7 import java.time.LocalDateTime; 5 import java.time.LocalDateTime;
8 import com.alibaba.excel.annotation.*; 6 import com.alibaba.excel.annotation.*;
9 7
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo; @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo;
2 2
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.*; 4 import lombok.*;
5 -import java.util.*;  
6 import jakarta.validation.constraints.*; 5 import jakarta.validation.constraints.*;
7 6
8 @Schema(description = "管理后台 - IoT 插件实例新增/修改 Request VO") 7 @Schema(description = "管理后台 - IoT 插件实例新增/修改 Request VO")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.framework.plugin;
  2 +
  3 +import cn.iocoder.yudao.module.iot.api.DeviceDataApi;
  4 +import cn.iocoder.yudao.module.iot.api.ServiceRegistry;
  5 +import lombok.extern.slf4j.Slf4j;
  6 +import org.springframework.context.annotation.Bean;
  7 +import org.springframework.context.annotation.Configuration;
  8 +
  9 +import javax.annotation.PostConstruct;
  10 +import javax.annotation.Resource;
  11 +
  12 +@Slf4j
  13 +@Configuration
  14 +public class ServiceRegistryConfiguration {
  15 +
  16 + @Resource
  17 + private DeviceDataApi deviceDataApi;
  18 +
  19 + @PostConstruct
  20 + public void init() {
  21 + // 将主程序中的 DeviceDataApi 实例注册到 ServiceRegistry
  22 + ServiceRegistry.registerService(DeviceDataApi.class, deviceDataApi);
  23 + log.info("[init][将 DeviceDataApi 实例注册到 ServiceRegistry 中]");
  24 + }
  25 +
  26 + /**
  27 + * 定义一个标记用的 Bean,用于表示 ServiceRegistry 已初始化完成
  28 + */
  29 + @Bean("serviceRegistryInitializedMarker")
  30 + public Object serviceRegistryInitializedMarker() {
  31 + // 返回任意对象即可,这里返回null都可以,但最好返回个实际对象
  32 + return new Object();
  33 + }
  34 +}
0 \ No newline at end of file 35 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java
1 package cn.iocoder.yudao.module.iot.framework.plugin; 1 package cn.iocoder.yudao.module.iot.framework.plugin;
2 2
3 -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.Greetings;  
4 import org.pf4j.spring.SpringPluginManager; 3 import org.pf4j.spring.SpringPluginManager;
5 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.Bean;
6 import org.springframework.context.annotation.Configuration; 5 import org.springframework.context.annotation.Configuration;
@@ -10,14 +9,9 @@ import org.springframework.context.annotation.DependsOn; @@ -10,14 +9,9 @@ import org.springframework.context.annotation.DependsOn;
10 public class SpringConfiguration { 9 public class SpringConfiguration {
11 10
12 @Bean 11 @Bean
  12 + @DependsOn("serviceRegistryInitializedMarker")
13 public SpringPluginManager pluginManager() { 13 public SpringPluginManager pluginManager() {
14 return new SpringPluginManager(); 14 return new SpringPluginManager();
15 } 15 }
16 16
17 - @Bean  
18 - @DependsOn("pluginManager")  
19 - public Greetings greetings() {  
20 - return new Greetings();  
21 - }  
22 -  
23 } 17 }
24 \ No newline at end of file 18 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java
@@ -20,7 +20,8 @@ public interface IotDeviceDataService { @@ -20,7 +20,8 @@ public interface IotDeviceDataService {
20 * 20 *
21 * @param productKey 产品 key 21 * @param productKey 产品 key
22 * @param deviceName 设备名称 22 * @param deviceName 设备名称
23 - * @param message 消息 23 + * @param message 消息
  24 + * <p>JSON 格式,参见 <a href="https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services?spm=a2c4g.11186623.0.0.3a3335aeUdzkz2#concept-mvc-4tw-y2b">...</a>
24 */ 25 */
25 void saveDeviceData(String productKey, String deviceName, String message); 26 void saveDeviceData(String productKey, String deviceName, String message);
26 27
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java
@@ -22,9 +22,7 @@ import org.springframework.stereotype.Service; @@ -22,9 +22,7 @@ import org.springframework.stereotype.Service;
22 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
23 import org.springframework.web.multipart.MultipartFile; 23 import org.springframework.web.multipart.MultipartFile;
24 24
25 -import java.nio.file.Files;  
26 import java.nio.file.Path; 25 import java.nio.file.Path;
27 -import java.nio.file.Paths;  
28 import java.util.concurrent.Executors; 26 import java.util.concurrent.Executors;
29 import java.util.concurrent.TimeUnit; 27 import java.util.concurrent.TimeUnit;
30 import java.util.jar.JarEntry; 28 import java.util.jar.JarEntry;
@@ -220,24 +218,24 @@ public class PluginInfoServiceImpl implements PluginInfoService { @@ -220,24 +218,24 @@ public class PluginInfoServiceImpl implements PluginInfoService {
220 pluginInfoMapper.updateById(pluginInfoDo); 218 pluginInfoMapper.updateById(pluginInfoDo);
221 } 219 }
222 220
223 - @PostConstruct  
224 - public void init() {  
225 - Executors.newSingleThreadScheduledExecutor().schedule(this::startPlugins, 3, TimeUnit.SECONDS);  
226 - }  
227 -  
228 - @SneakyThrows  
229 - private void startPlugins() {  
230 - for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) {  
231 - if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {  
232 - continue;  
233 - }  
234 - log.info("start plugin:{}", pluginInfoDO.getPluginId());  
235 - try {  
236 - pluginManager.startPlugin(pluginInfoDO.getPluginId());  
237 - } catch (Exception e) {  
238 - log.error("start plugin error", e);  
239 - }  
240 - }  
241 - } 221 +// @PostConstruct
  222 +// public void init() {
  223 +// Executors.newSingleThreadScheduledExecutor().schedule(this::startPlugins, 3, TimeUnit.SECONDS);
  224 +// }
  225 +//
  226 +// @SneakyThrows
  227 +// private void startPlugins() {
  228 +// for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) {
  229 +// if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
  230 +// continue;
  231 +// }
  232 +// log.info("start plugin:{}", pluginInfoDO.getPluginId());
  233 +// try {
  234 +// pluginManager.startPlugin(pluginInfoDO.getPluginId());
  235 +// } catch (Exception e) {
  236 +// log.error("start plugin error", e);
  237 +// }
  238 +// }
  239 +// }
242 240
243 } 241 }
244 \ No newline at end of file 242 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
@@ -122,7 +122,7 @@ public class IotProductServiceImpl implements IotProductService { @@ -122,7 +122,7 @@ public class IotProductServiceImpl implements IotProductService {
122 thingModelFunctionService.createSuperTableDataModel(id); 122 thingModelFunctionService.createSuperTableDataModel(id);
123 // 3.2 创建物模型日志超级表数据模型 123 // 3.2 创建物模型日志超级表数据模型
124 thingModelMessageService.createSuperTable(id); 124 thingModelMessageService.createSuperTable(id);
125 - }x 125 + }
126 productMapper.updateById(updateObj); 126 productMapper.updateById(updateObj);
127 } 127 }
128 128
yudao-module-iot/yudao-module-iot-plugin-api/pom.xml deleted
1 -<?xml version="1.0" encoding="UTF-8"?>  
2 -<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
3 - xmlns="http://maven.apache.org/POM/4.0.0"  
4 - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
5 - <modelVersion>4.0.0</modelVersion>  
6 - <groupId>cn.iocoder.boot</groupId>  
7 - <artifactId>yudao-module-iot-plugin-api</artifactId>  
8 - <version>0.0.1</version>  
9 - <packaging>jar</packaging>  
10 -  
11 - <name>${project.artifactId}</name>  
12 - <description>  
13 - 物联网 模块插件 API,暴露给其它模块调用  
14 - </description>  
15 -  
16 - <properties>  
17 - <pf4j-spring.version>0.9.0</pf4j-spring.version>  
18 - </properties>  
19 -  
20 - <dependencies>  
21 - <!-- PF4J Spring 集成 -->  
22 - <dependency>  
23 - <groupId>org.pf4j</groupId>  
24 - <artifactId>pf4j-spring</artifactId>  
25 - <version>${pf4j-spring.version}</version>  
26 - <scope>provided</scope>  
27 - </dependency>  
28 - </dependencies>  
29 -  
30 -</project>  
yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/Greeting.java deleted
1 -/*  
2 - * Copyright (C) 2012-present the original author or authors.  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package cn.iocoder.yudao.module.iot.api;  
17 -  
18 -import org.pf4j.ExtensionPoint;  
19 -  
20 -/**  
21 - * @author Decebal Suiu  
22 - */  
23 -public interface Greeting extends ExtensionPoint {  
24 -  
25 - String getGreeting();  
26 -  
27 -}  
yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java deleted
1 -/**  
2 - * 占位  
3 - *  
4 - * TODO 芋艿:后续删除  
5 - */  
6 -package cn.iocoder.yudao.module.iot.api;  
yudao-module-iot/yudao-module-iot-plugin/pom.xml
@@ -2,19 +2,29 @@ @@ -2,19 +2,29 @@
2 <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns="http://maven.apache.org/POM/4.0.0"
4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5 - <modelVersion>4.0.0</modelVersion>  
6 - <groupId>cn.iocoder.boot</groupId>  
7 - <artifactId>yudao-module-iot-plugin</artifactId>  
8 - <version>0.0.1</version>  
9 - <packaging>pom</packaging>  
10 - 5 +<!-- <modelVersion>4.0.0</modelVersion>-->
  6 +<!-- <groupId>cn.iocoder.boot</groupId>-->
  7 +<!-- <artifactId>yudao-module-iot-plugin</artifactId>-->
  8 +<!-- <version>0.0.1</version>-->
  9 +<!-- <packaging>pom</packaging>-->
  10 + <parent>
  11 + <artifactId>yudao-module-iot</artifactId>
  12 + <groupId>cn.iocoder.boot</groupId>
  13 + <version>${revision}</version>
  14 + </parent>
11 <modules> 15 <modules>
  16 + <module>yudao-module-iot-demo-plugin</module>
12 <module>yudao-module-iot-http-plugin</module> 17 <module>yudao-module-iot-http-plugin</module>
13 </modules> 18 </modules>
14 19
  20 + <modelVersion>4.0.0</modelVersion>
  21 +
  22 + <artifactId>yudao-module-iot-plugin</artifactId>
  23 + <packaging>pom</packaging>
  24 +
15 <name>${project.artifactId}</name> 25 <name>${project.artifactId}</name>
16 <description> 26 <description>
17 - 物联网模块 - 插件模块 27 + 物联网 插件 模块
18 </description> 28 </description>
19 29
20 </project> 30 </project>
21 \ No newline at end of file 31 \ No newline at end of file
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/plugin.properties 0 → 100644
  1 +plugin.id=demo-plugin
  2 +plugin.class=cn.iocoder.yudao.module.iot.plugin.DemoPlugin
  3 +plugin.version=0.0.1
  4 +plugin.provider=ahh
  5 +plugin.dependencies=
  6 +plugin.description=demo-plugin
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3 + xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="
  4 + http://maven.apache.org/POM/4.0.0
  5 + http://maven.apache.org/xsd/maven-4.0.0.xsd">
  6 + <parent>
  7 + <artifactId>yudao-module-iot-plugin</artifactId>
  8 + <groupId>cn.iocoder.boot</groupId>
  9 + <version>${revision}</version>
  10 + </parent>
  11 + <modelVersion>4.0.0</modelVersion>
  12 + <packaging>jar</packaging>
  13 +
  14 + <artifactId>yudao-module-iot-demo-plugin</artifactId>
  15 +
  16 + <name>${project.artifactId}</name>
  17 + <description>
  18 + 物联网 插件模块 - demo 插件
  19 + </description>
  20 +
  21 + <properties>
  22 + <!-- 插件相关 -->
  23 + <plugin.id>demo-plugin</plugin.id>
  24 + <plugin.class>cn.iocoder.yudao.module.iot.plugin.DemoPlugin</plugin.class>
  25 + <plugin.version>0.0.1</plugin.version>
  26 + <plugin.provider>ahh</plugin.provider>
  27 + <plugin.dependencies/>
  28 + </properties>
  29 +
  30 + <build>
  31 + <plugins>
  32 + <!-- DOESN'T WORK WITH MAVEN 3 (I defined the plugin metadata in properties section)
  33 + <plugin>
  34 + <groupId>org.codehaus.mojo</groupId>
  35 + <artifactId>properties-maven-plugin</artifactId>
  36 + <version>1.0-alpha-2</version>
  37 + <executions>
  38 + <execution>
  39 + <phase>initialize</phase>
  40 + <goals>
  41 + <goal>read-project-properties</goal>
  42 + </goals>
  43 + <configuration>
  44 + <files>
  45 + <file>plugin.properties</file>
  46 + </files>
  47 + </configuration>
  48 + </execution>
  49 + </executions>
  50 + </plugin>
  51 + -->
  52 +
  53 + <plugin>
  54 + <groupId>org.apache.maven.plugins</groupId>
  55 + <artifactId>maven-antrun-plugin</artifactId>
  56 + <version>1.6</version>
  57 + <executions>
  58 + <execution>
  59 + <id>unzip jar file</id>
  60 + <phase>package</phase>
  61 + <configuration>
  62 + <target>
  63 + <unzip src="target/${project.artifactId}-${project.version}.${project.packaging}" dest="target/plugin-classes" />
  64 + </target>
  65 + </configuration>
  66 + <goals>
  67 + <goal>run</goal>
  68 + </goals>
  69 + </execution>
  70 + </executions>
  71 + </plugin>
  72 +
  73 + <plugin>
  74 + <artifactId>maven-assembly-plugin</artifactId>
  75 + <version>2.3</version>
  76 + <configuration>
  77 + <descriptors>
  78 + <descriptor>
  79 + src/main/assembly/assembly.xml
  80 + </descriptor>
  81 + </descriptors>
  82 + <appendAssemblyId>false</appendAssemblyId>
  83 + </configuration>
  84 + <executions>
  85 + <execution>
  86 + <id>make-assembly</id>
  87 + <phase>package</phase>
  88 + <goals>
  89 + <goal>attached</goal>
  90 + </goals>
  91 + </execution>
  92 + </executions>
  93 + </plugin>
  94 +
  95 + <plugin>
  96 + <groupId>org.apache.maven.plugins</groupId>
  97 + <artifactId>maven-jar-plugin</artifactId>
  98 + <version>2.4</version>
  99 + <configuration>
  100 + <archive>
  101 + <manifestEntries>
  102 + <Plugin-Id>${plugin.id}</Plugin-Id>
  103 + <Plugin-Class>${plugin.class}</Plugin-Class>
  104 + <Plugin-Version>${plugin.version}</Plugin-Version>
  105 + <Plugin-Provider>${plugin.provider}</Plugin-Provider>
  106 + <Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies>
  107 + </manifestEntries>
  108 + </archive>
  109 + </configuration>
  110 + </plugin>
  111 +
  112 + <plugin>
  113 + <artifactId>maven-deploy-plugin</artifactId>
  114 + <configuration>
  115 + <skip>true</skip>
  116 + </configuration>
  117 + </plugin>
  118 + </plugins>
  119 + </build>
  120 +
  121 + <dependencies>
  122 + <!-- 其他依赖项 -->
  123 + <dependency>
  124 + <groupId>org.springframework.boot</groupId>
  125 + <artifactId>spring-boot-starter-web</artifactId>
  126 + <version>${spring.boot.version}</version>
  127 + <scope>provided</scope>
  128 + </dependency>
  129 + <!-- PF4J Spring 集成 -->
  130 + <dependency>
  131 + <groupId>org.pf4j</groupId>
  132 + <artifactId>pf4j-spring</artifactId>
  133 + <scope>provided</scope>
  134 + </dependency>
  135 + <!-- 项目依赖 -->
  136 + <dependency>
  137 + <groupId>cn.iocoder.boot</groupId>
  138 + <artifactId>yudao-module-iot-api</artifactId>
  139 + <version>${revision}</version>
  140 + </dependency>
  141 + <dependency>
  142 + <groupId>org.projectlombok</groupId>
  143 + <artifactId>lombok</artifactId>
  144 + <version>${lombok.version}</version>
  145 + <scope>provided</scope>
  146 + </dependency>
  147 + </dependencies>
  148 +</project>
0 \ No newline at end of file 149 \ No newline at end of file
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/assembly/assembly.xml 0 → 100644
  1 +<assembly>
  2 + <id>plugin</id>
  3 + <formats>
  4 + <format>zip</format>
  5 + </formats>
  6 + <includeBaseDirectory>false</includeBaseDirectory>
  7 + <dependencySets>
  8 + <dependencySet>
  9 + <useProjectArtifact>false</useProjectArtifact>
  10 + <scope>runtime</scope>
  11 + <outputDirectory>lib</outputDirectory>
  12 + <includes>
  13 + <include>*:jar:*</include>
  14 + </includes>
  15 + </dependencySet>
  16 + </dependencySets>
  17 + <!--
  18 + <fileSets>
  19 + <fileSet>
  20 + <directory>target/classes</directory>
  21 + <outputDirectory>classes</outputDirectory>
  22 + </fileSet>
  23 + </fileSets>
  24 + -->
  25 + <fileSets>
  26 + <fileSet>
  27 + <directory>target/plugin-classes</directory>
  28 + <outputDirectory>classes</outputDirectory>
  29 + </fileSet>
  30 + </fileSets>
  31 +</assembly>
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/DemoPlugin.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.plugin;
  2 +
  3 +import com.sun.net.httpserver.HttpServer;
  4 +import lombok.extern.slf4j.Slf4j;
  5 +import org.pf4j.Plugin;
  6 +import org.pf4j.PluginWrapper;
  7 +import org.pf4j.RuntimeMode;
  8 +
  9 +import java.io.IOException;
  10 +import java.io.OutputStream;
  11 +import java.net.InetSocketAddress;
  12 +
  13 +/**
  14 + * 一个启动 HTTP 服务器的简单插件。
  15 + */
  16 +@Slf4j
  17 +public class DemoPlugin extends Plugin {
  18 +
  19 + private HttpServer server;
  20 +
  21 + public DemoPlugin(PluginWrapper wrapper) {
  22 + super(wrapper);
  23 + }
  24 +
  25 + @Override
  26 + public void start() {
  27 + log.info("Demo 插件启动");
  28 + // for testing the development mode
  29 + if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) {
  30 + log.info("DemoPlugin in DEVELOPMENT mode");
  31 + }
  32 + startDemoServer();
  33 + }
  34 +
  35 + @Override
  36 + public void stop() {
  37 + log.info("Demo 插件停止");
  38 + stopDemoServer();
  39 + }
  40 +
  41 + private void startDemoServer() {
  42 + try {
  43 + server = HttpServer.create(new InetSocketAddress(9081), 0);
  44 + server.createContext("/", exchange -> {
  45 + String response = "Hello from DemoPlugin";
  46 + exchange.sendResponseHeaders(200, response.getBytes().length);
  47 + OutputStream os = exchange.getResponseBody();
  48 + os.write(response.getBytes());
  49 + os.close();
  50 + });
  51 + server.setExecutor(null);
  52 + server.start();
  53 + log.info("HTTP 服务器启动成功,端口为 9081");
  54 + log.info("访问地址为 http://127.0.0.1:9081/");
  55 + } catch (IOException e) {
  56 + log.error("HTTP 服务器启动失败", e);
  57 + }
  58 + }
  59 +
  60 + private void stopDemoServer() {
  61 + if (server != null) {
  62 + server.stop(0);
  63 + log.info("HTTP 服务器停止成功");
  64 + }
  65 + }
  66 +
  67 +// @Extension
  68 +// public static class WelcomeGreeting implements Greeting {
  69 +//
  70 +// @Override
  71 +// public String getGreeting() {
  72 +// return "Welcome to DemoPlugin";
  73 +// }
  74 +//
  75 +// }
  76 +
  77 +}
0 \ No newline at end of file 78 \ No newline at end of file
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties
@@ -3,3 +3,4 @@ plugin.class=cn.iocoder.yudao.module.iot.plugin.HttpPlugin @@ -3,3 +3,4 @@ plugin.class=cn.iocoder.yudao.module.iot.plugin.HttpPlugin
3 plugin.version=0.0.1 3 plugin.version=0.0.1
4 plugin.provider=ahh 4 plugin.provider=ahh
5 plugin.dependencies= 5 plugin.dependencies=
  6 +plugin.description=http-plugin-0.0.1
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml
@@ -3,14 +3,20 @@ @@ -3,14 +3,20 @@
3 xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation=" 3 xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="
4 http://maven.apache.org/POM/4.0.0 4 http://maven.apache.org/POM/4.0.0
5 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  6 + <parent>
  7 + <artifactId>yudao-module-iot-plugin</artifactId>
  8 + <groupId>cn.iocoder.boot</groupId>
  9 + <version>${revision}</version>
  10 + </parent>
6 <modelVersion>4.0.0</modelVersion> 11 <modelVersion>4.0.0</modelVersion>
7 - <groupId>cn.iocoder.boot</groupId>  
8 - <artifactId>yudao-module-iot-http-plugin</artifactId>  
9 - <version>0.0.1</version>  
10 <packaging>jar</packaging> 12 <packaging>jar</packaging>
11 13
  14 + <artifactId>yudao-module-iot-http-plugin</artifactId>
  15 +
12 <name>${project.artifactId}</name> 16 <name>${project.artifactId}</name>
13 - <description>物联网 模块 - http 插件</description> 17 + <description>
  18 + 物联网 插件模块 - http 插件
  19 + </description>
14 20
15 <properties> 21 <properties>
16 <!-- 插件相关 --> 22 <!-- 插件相关 -->
@@ -19,50 +25,8 @@ @@ -19,50 +25,8 @@
19 <plugin.version>0.0.1</plugin.version> 25 <plugin.version>0.0.1</plugin.version>
20 <plugin.provider>ahh</plugin.provider> 26 <plugin.provider>ahh</plugin.provider>
21 <plugin.dependencies/> 27 <plugin.dependencies/>
22 -  
23 - <!-- Maven 相关 -->  
24 - <java.version>17</java.version>  
25 - <maven.compiler.source>${java.version}</maven.compiler.source>  
26 - <maven.compiler.target>${java.version}</maven.compiler.target>  
27 - <maven-antrun-plugin.version>1.6</maven-antrun-plugin.version>  
28 - <maven-assembly-plugin.version>2.3</maven-assembly-plugin.version>  
29 - <maven-jar-plugin.version>2.4</maven-jar-plugin.version>  
30 - <pf4j-spring.version>0.9.0</pf4j-spring.version>  
31 - <!-- 看看咋放到 bom 里 -->  
32 - <lombok.version>1.18.34</lombok.version>  
33 - <spring.boot.version>3.3.1</spring.boot.version>  
34 - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
35 </properties> 28 </properties>
36 29
37 - <dependencies>  
38 - <!-- 其他依赖项 -->  
39 - <dependency>  
40 - <groupId>org.springframework.boot</groupId>  
41 - <artifactId>spring-boot-starter-web</artifactId>  
42 - <version>${spring.boot.version}</version>  
43 - <scope>provided</scope>  
44 - </dependency>  
45 - <!-- PF4J Spring 集成 -->  
46 - <dependency>  
47 - <groupId>org.pf4j</groupId>  
48 - <artifactId>pf4j-spring</artifactId>  
49 - <version>${pf4j-spring.version}</version>  
50 - <scope>provided</scope>  
51 - </dependency>  
52 - <!-- 项目依赖 -->  
53 - <dependency>  
54 - <groupId>cn.iocoder.boot</groupId>  
55 - <artifactId>yudao-module-iot-plugin-api</artifactId>  
56 - <version>${project.version}</version>  
57 - </dependency>  
58 - <dependency>  
59 - <groupId>org.projectlombok</groupId>  
60 - <artifactId>lombok</artifactId>  
61 - <version>${lombok.version}</version>  
62 - <scope>provided</scope>  
63 - </dependency>  
64 - </dependencies>  
65 -  
66 <build> 30 <build>
67 <plugins> 31 <plugins>
68 <!-- DOESN'T WORK WITH MAVEN 3 (I defined the plugin metadata in properties section) 32 <!-- DOESN'T WORK WITH MAVEN 3 (I defined the plugin metadata in properties section)
@@ -89,14 +53,15 @@ @@ -89,14 +53,15 @@
89 <plugin> 53 <plugin>
90 <groupId>org.apache.maven.plugins</groupId> 54 <groupId>org.apache.maven.plugins</groupId>
91 <artifactId>maven-antrun-plugin</artifactId> 55 <artifactId>maven-antrun-plugin</artifactId>
92 - <version>${maven-antrun-plugin.version}</version> 56 + <version>1.6</version>
93 <executions> 57 <executions>
94 <execution> 58 <execution>
95 <id>unzip jar file</id> 59 <id>unzip jar file</id>
96 <phase>package</phase> 60 <phase>package</phase>
97 <configuration> 61 <configuration>
98 <target> 62 <target>
99 - <unzip src="target/${project.artifactId}-${project.version}.${project.packaging}" dest="target/plugin-classes" /> 63 + <unzip src="target/${project.artifactId}-${project.version}.${project.packaging}"
  64 + dest="target/plugin-classes"/>
100 </target> 65 </target>
101 </configuration> 66 </configuration>
102 <goals> 67 <goals>
@@ -108,7 +73,7 @@ @@ -108,7 +73,7 @@
108 73
109 <plugin> 74 <plugin>
110 <artifactId>maven-assembly-plugin</artifactId> 75 <artifactId>maven-assembly-plugin</artifactId>
111 - <version>${maven-assembly-plugin.version}</version> 76 + <version>2.3</version>
112 <configuration> 77 <configuration>
113 <descriptors> 78 <descriptors>
114 <descriptor> 79 <descriptor>
@@ -131,7 +96,7 @@ @@ -131,7 +96,7 @@
131 <plugin> 96 <plugin>
132 <groupId>org.apache.maven.plugins</groupId> 97 <groupId>org.apache.maven.plugins</groupId>
133 <artifactId>maven-jar-plugin</artifactId> 98 <artifactId>maven-jar-plugin</artifactId>
134 - <version>${maven-jar-plugin.version}</version> 99 + <version>2.4</version>
135 <configuration> 100 <configuration>
136 <archive> 101 <archive>
137 <manifestEntries> 102 <manifestEntries>
@@ -153,4 +118,35 @@ @@ -153,4 +118,35 @@
153 </plugin> 118 </plugin>
154 </plugins> 119 </plugins>
155 </build> 120 </build>
  121 +
  122 + <dependencies>
  123 + <!-- 其他依赖项 -->
  124 + <dependency>
  125 + <groupId>org.springframework.boot</groupId>
  126 + <artifactId>spring-boot-starter-web</artifactId>
  127 + </dependency>
  128 + <!-- PF4J Spring 集成 -->
  129 + <dependency>
  130 + <groupId>org.pf4j</groupId>
  131 + <artifactId>pf4j-spring</artifactId>
  132 + <scope>provided</scope>
  133 + </dependency>
  134 + <!-- 项目依赖 -->
  135 + <dependency>
  136 + <groupId>cn.iocoder.boot</groupId>
  137 + <artifactId>yudao-module-iot-api</artifactId>
  138 + <version>${revision}</version>
  139 + </dependency>
  140 + <dependency>
  141 + <groupId>org.projectlombok</groupId>
  142 + <artifactId>lombok</artifactId>
  143 + <version>${lombok.version}</version>
  144 + <scope>provided</scope>
  145 + </dependency>
  146 + <dependency>
  147 + <groupId>io.netty</groupId>
  148 + <artifactId>netty-all</artifactId>
  149 + <version>4.1.63.Final</version> <!-- 版本可根据需要调整 -->
  150 + </dependency>
  151 + </dependencies>
156 </project> 152 </project>
157 \ No newline at end of file 153 \ No newline at end of file
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/assembly/assembly.xml
1 -<!--  
2 - Describes the plugin archive  
3 -  
4 - @author Decebal Suiu  
5 - @version 1.0  
6 --->  
7 <assembly> 1 <assembly>
8 <id>plugin</id> 2 <id>plugin</id>
9 <formats> 3 <formats>
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.plugin;
  2 +
  3 +import cn.hutool.json.JSONObject;
  4 +import cn.hutool.json.JSONUtil;
  5 +import cn.iocoder.yudao.module.iot.api.DeviceDataApi;
  6 +import io.netty.buffer.Unpooled;
  7 +import io.netty.channel.ChannelFutureListener;
  8 +import io.netty.channel.ChannelHandlerContext;
  9 +import io.netty.channel.SimpleChannelInboundHandler;
  10 +import io.netty.handler.codec.http.*;
  11 +import io.netty.util.CharsetUtil;
  12 +
  13 +/**
  14 + * 基于 Netty 的 HTTP 处理器,用于接收设备上报的数据并调用主程序的 DeviceDataApi 接口进行处理。
  15 + * <p>
  16 + * 请求格式:
  17 + * POST /sys/{productKey}/{deviceName}/thing/event/property/post
  18 + * 请求体为 JSON 格式数据。
  19 + * <p>
  20 + * 返回结果为 JSON 格式,包含统一的 code、data、id、message、method、version 字段。
  21 + */
  22 +public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
  23 +
  24 + private final DeviceDataApi deviceDataApi;
  25 +
  26 + public HttpHandler(DeviceDataApi deviceDataApi) {
  27 + this.deviceDataApi = deviceDataApi;
  28 + }
  29 +
  30 + @Override
  31 + protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
  32 + String uri = request.uri();
  33 +
  34 + // 期望的路径格式: /sys/{productKey}/{deviceName}/thing/event/property/post
  35 + // 使用 "/" 拆分路径
  36 + String[] parts = uri.split("/");
  37 +
  38 + /*
  39 + 拆分结果示例:
  40 + parts[0] = ""
  41 + parts[1] = "sys"
  42 + parts[2] = productKey
  43 + parts[3] = deviceName
  44 + parts[4] = "thing"
  45 + parts[5] = "event"
  46 + parts[6] = "property"
  47 + parts[7] = "post"
  48 + */
  49 + boolean isCorrectPath = parts.length == 8
  50 + && "sys".equals(parts[1])
  51 + && "thing".equals(parts[4])
  52 + && "event".equals(parts[5])
  53 + && "property".equals(parts[6])
  54 + && "post".equals(parts[7]);
  55 +
  56 + if (!isCorrectPath) {
  57 + // 如果路径不匹配,返回 404 错误
  58 + writeResponse(ctx, HttpResponseStatus.NOT_FOUND, "Not Found");
  59 + return;
  60 + }
  61 +
  62 + String productKey = parts[2];
  63 + String deviceName = parts[3];
  64 +
  65 + // 从请求中获取原始数据
  66 + String requestBody = request.content().toString(CharsetUtil.UTF_8);
  67 +
  68 + // 尝试解析请求数据为 JSON 对象
  69 + JSONObject jsonData;
  70 + try {
  71 + jsonData = JSONUtil.parseObj(requestBody);
  72 + } catch (Exception e) {
  73 + // 数据不是合法的 JSON 格式,返回 400 错误
  74 + JSONObject res = createResponseJson(
  75 + 400,
  76 + new JSONObject(),
  77 + null,
  78 + "请求数据不是合法的 JSON 格式: " + e.getMessage(),
  79 + "thing.event.property.post",
  80 + "1.0"
  81 + );
  82 + writeResponse(ctx, HttpResponseStatus.BAD_REQUEST, res.toString());
  83 + return;
  84 + }
  85 +
  86 + // 获取请求中的 id 字段,若不存在则为 null
  87 + String id = jsonData.getStr("id", null);
  88 +
  89 + try {
  90 + // 调用主程序的接口保存数据
  91 + deviceDataApi.saveDeviceData(productKey, deviceName, jsonData.toString());
  92 +
  93 + // 构造成功响应内容
  94 + JSONObject successRes = createResponseJson(
  95 + 200,
  96 + new JSONObject(),
  97 + id,
  98 + "success",
  99 + "thing.event.property.post",
  100 + "1.0"
  101 + );
  102 + writeResponse(ctx, HttpResponseStatus.OK, successRes.toString());
  103 + } catch (Exception e) {
  104 + // 保存数据过程中出现异常,返回 500 错误
  105 + JSONObject errorRes = createResponseJson(
  106 + 500,
  107 + new JSONObject(),
  108 + id,
  109 + "The format of result is error!",
  110 + "thing.event.property.post",
  111 + "1.0"
  112 + );
  113 + writeResponse(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR, errorRes.toString());
  114 + }
  115 + }
  116 +
  117 + /**
  118 + * 创建标准化的响应 JSON 对象
  119 + *
  120 + * @param code 响应状态码(业务层面的)
  121 + * @param data 返回的数据对象(JSON)
  122 + * @param id 请求的 id(可选)
  123 + * @param message 返回的提示信息
  124 + * @param method 返回的 method 标识
  125 + * @param version 返回的版本号
  126 + * @return 构造好的 JSON 对象
  127 + */
  128 + private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, String version) {
  129 + JSONObject res = new JSONObject();
  130 + res.set("code", code);
  131 + res.set("data", data != null ? data : new JSONObject()); // 确保 data 不为 null
  132 + res.set("id", id);
  133 + res.set("message", message);
  134 + res.set("method", method);
  135 + res.set("version", version);
  136 + return res;
  137 + }
  138 +
  139 + /**
  140 + * 向客户端返回 HTTP 响应的辅助方法。
  141 + *
  142 + * @param ctx 通道上下文
  143 + * @param status HTTP 响应状态码(网络层面的)
  144 + * @param content 响应内容(JSON 字符串或其他文本)
  145 + */
  146 + private void writeResponse(ChannelHandlerContext ctx, HttpResponseStatus status, String content) {
  147 + FullHttpResponse response = new DefaultFullHttpResponse(
  148 + HttpVersion.HTTP_1_1,
  149 + status,
  150 + Unpooled.copiedBuffer(content, CharsetUtil.UTF_8)
  151 + );
  152 +
  153 + // 设置响应头为 JSON 类型和正确的编码
  154 + response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8");
  155 + response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
  156 +
  157 + // 发送响应并在发送完成后关闭连接
  158 + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
  159 + }
  160 +}
0 \ No newline at end of file 161 \ No newline at end of file
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java
1 package cn.iocoder.yudao.module.iot.plugin; 1 package cn.iocoder.yudao.module.iot.plugin;
2 2
3 -import cn.iocoder.yudao.module.iot.api.Greeting;  
4 -import com.sun.net.httpserver.HttpServer; 3 +import cn.iocoder.yudao.module.iot.api.DeviceDataApi;
  4 +import cn.iocoder.yudao.module.iot.api.ServiceRegistry;
  5 +import io.netty.bootstrap.ServerBootstrap;
  6 +import io.netty.channel.*;
  7 +import io.netty.channel.nio.NioEventLoopGroup;
  8 +import io.netty.channel.socket.nio.NioServerSocketChannel;
  9 +import io.netty.handler.codec.http.*;
5 import lombok.extern.slf4j.Slf4j; 10 import lombok.extern.slf4j.Slf4j;
6 -import org.apache.commons.lang.StringUtils;  
7 -import org.pf4j.Extension;  
8 -import org.pf4j.Plugin;  
9 import org.pf4j.PluginWrapper; 11 import org.pf4j.PluginWrapper;
10 -import org.pf4j.RuntimeMode; 12 +import org.pf4j.Plugin;
11 13
12 -import java.io.IOException;  
13 -import java.io.OutputStream;  
14 -import java.net.InetSocketAddress; 14 +import java.util.concurrent.ExecutorService;
  15 +import java.util.concurrent.Executors;
15 16
16 -/**  
17 - * 一个启动 HTTP 服务器的简单插件。  
18 - */  
19 @Slf4j 17 @Slf4j
20 public class HttpPlugin extends Plugin { 18 public class HttpPlugin extends Plugin {
21 19
22 - private HttpServer server; 20 + private static final int PORT = 8092;
  21 + private final ExecutorService executorService;
  22 + private DeviceDataApi deviceDataApi; // 用于保存设备数据的 API
23 23
24 public HttpPlugin(PluginWrapper wrapper) { 24 public HttpPlugin(PluginWrapper wrapper) {
25 super(wrapper); 25 super(wrapper);
  26 + this.executorService = Executors.newSingleThreadExecutor(); // 创建单线程池
26 } 27 }
27 28
28 @Override 29 @Override
29 public void start() { 30 public void start() {
30 log.info("HttpPlugin.start()"); 31 log.info("HttpPlugin.start()");
31 - // for testing the development mode  
32 - if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) {  
33 - log.info("HttpPlugin in DEVELOPMENT mode"); 32 +
  33 + // 从 ServiceRegistry 中获取主程序暴露的 DeviceDataApi 接口实例
  34 + deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class);
  35 + if (deviceDataApi == null) {
  36 + log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!");
  37 + return;
34 } 38 }
35 - startHttpServer(); 39 +
  40 + // 异步启动 Netty 服务器
  41 + executorService.submit(this::startHttpServer);
36 } 42 }
37 43
38 @Override 44 @Override
39 public void stop() { 45 public void stop() {
40 log.info("HttpPlugin.stop()"); 46 log.info("HttpPlugin.stop()");
41 - stopHttpServer(); 47 + executorService.shutdownNow(); // 停止线程池
42 } 48 }
43 49
  50 + // 启动 HTTP 服务
44 private void startHttpServer() { 51 private void startHttpServer() {
45 - try {  
46 - server = HttpServer.create(new InetSocketAddress(9081), 0);  
47 - server.createContext("/", exchange -> {  
48 - String response = "Welcome to PF4J HTTP Server";  
49 - exchange.sendResponseHeaders(200, response.getBytes().length);  
50 - OutputStream os = exchange.getResponseBody();  
51 - os.write(response.getBytes());  
52 - os.close();  
53 - });  
54 - server.setExecutor(null);  
55 - server.start();  
56 - log.info("HTTP server started on port 9081");  
57 - } catch (IOException e) {  
58 - log.error("Error starting HTTP server", e);  
59 - }  
60 - } 52 + EventLoopGroup bossGroup = new NioEventLoopGroup(1);
  53 + EventLoopGroup workerGroup = new NioEventLoopGroup();
61 54
62 - private void stopHttpServer() {  
63 - if (server != null) {  
64 - server.stop(0);  
65 - log.info("HTTP server stopped");  
66 - }  
67 - }  
68 -  
69 - @Extension  
70 - public static class WelcomeGreeting implements Greeting { 55 + try {
  56 + ServerBootstrap bootstrap = new ServerBootstrap();
  57 + bootstrap.group(bossGroup, workerGroup)
  58 + .channel(NioServerSocketChannel.class)
  59 + .childHandler(new ChannelInitializer<Channel>() {
  60 + @Override
  61 + protected void initChannel(Channel channel) {
  62 + channel.pipeline().addLast(new HttpServerCodec());
  63 + channel.pipeline().addLast(new HttpObjectAggregator(65536));
  64 + // 将从 ServiceRegistry 获取的 deviceDataApi 传入处理器
  65 + channel.pipeline().addLast(new HttpHandler(deviceDataApi));
  66 + }
  67 + });
71 68
72 - @Override  
73 - public String getGreeting() {  
74 - return "Welcome to PF4J"; 69 + // 绑定端口并启动服务器
  70 + ChannelFuture future = bootstrap.bind(PORT).sync();
  71 + log.info("HTTP 服务器启动成功,端口为: {}", PORT);
  72 + future.channel().closeFuture().sync();
  73 + } catch (InterruptedException e) {
  74 + Thread.currentThread().interrupt();
  75 + log.error("HTTP 服务启动中断", e);
  76 + } finally {
  77 + bossGroup.shutdownGracefully();
  78 + workerGroup.shutdownGracefully();
75 } 79 }
76 -  
77 } 80 }
78 -  
79 -}  
80 \ No newline at end of file 81 \ No newline at end of file
  82 +}