Commit 8c84ac9d8a88dd73d03746274f4f6e2de7004668

Authored by 安浩浩
1 parent 88088c79

【新增功能】 mqtt 数据接收

Showing 13 changed files with 224 additions and 23 deletions
yudao-module-iot/yudao-module-iot-biz/pom.xml
... ... @@ -25,6 +25,11 @@
25 25 <version>${revision}</version>
26 26 </dependency>
27 27  
  28 + <dependency>
  29 + <groupId>cn.iocoder.boot</groupId>
  30 + <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
  31 + </dependency>
  32 +
28 33 <!-- Web 相关 -->
29 34 <dependency>
30 35 <groupId>cn.iocoder.boot</groupId>
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Builder;
  5 +import lombok.Data;
  6 +import lombok.NoArgsConstructor;
  7 +
  8 +import java.util.HashMap;
  9 +import java.util.Map;
  10 +
  11 +/**
  12 + * 物模型消息
  13 + */
  14 +@Data
  15 +@NoArgsConstructor
  16 +@AllArgsConstructor
  17 +@Builder
  18 +public class ThingModelMessage {
  19 +
  20 + /**
  21 + * 消息ID
  22 + */
  23 + private String id;
  24 +
  25 + /**
  26 + * 扩展功能的参数
  27 + */
  28 + private Object sys;
  29 +
  30 + /**
  31 + * 请求方法 例如:thing.event.property.post
  32 + */
  33 + private String method;
  34 +
  35 + /**
  36 + * 请求参数
  37 + */
  38 + private Object params;
  39 +
  40 + /**
  41 + * 属性上报时间戳
  42 + */
  43 + private Long time;
  44 +
  45 + /**
  46 + * 设备信息
  47 + */
  48 + private String productKey;
  49 +
  50 + /**
  51 + * 设备名称
  52 + */
  53 + private String deviceName;
  54 +
  55 + /**
  56 + * 设备 key
  57 + */
  58 + private String deviceKey;
  59 +
  60 + /**
  61 + * 转换为 Map 类型
  62 + */
  63 + public Map<String, Object> dataToMap() {
  64 + Map<String, Object> mapData = new HashMap<>();
  65 + if (params instanceof Map) {
  66 + ((Map<?, ?>) params).forEach((key, value) -> mapData.put(key.toString(), value));
  67 + }
  68 + return mapData;
  69 + }
  70 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java
1 1 package cn.iocoder.yudao.module.iot.emq.service;
2 2  
  3 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  4 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService;
  5 +import jakarta.annotation.Resource;
3 6 import lombok.extern.slf4j.Slf4j;
4 7 import org.eclipse.paho.client.mqttv3.MqttClient;
5 8 import org.eclipse.paho.client.mqttv3.MqttMessage;
... ... @@ -16,13 +19,20 @@ import org.springframework.stereotype.Service;
16 19 @Service
17 20 public class EmqxServiceImpl implements EmqxService {
18 21  
  22 + @Resource
  23 + private IotDeviceDataService iotDeviceDataService;
  24 +
19 25 // TODO 多线程处理消息
20 26 @Override
21 27 public void subscribeCallback(String topic, MqttMessage mqttMessage) {
22 28 log.info("收到消息,主题: {}, 内容: {}", topic, new String(mqttMessage.getPayload()));
23 29 // 根据不同的主题,处理不同的业务逻辑
24 30 if (topic.contains("/property/post")) {
25   - // 设备上报数据
  31 + // 设备上报数据 topic /sys/f13f57c63e9/dianbiao1/thing/event/property/post
  32 + String productKey = topic.split("/")[2];
  33 + String deviceName = topic.split("/")[3];
  34 + String message = new String(mqttMessage.getPayload());
  35 + iotDeviceDataService.saveDeviceData(productKey, deviceName, message);
26 36 }
27 37 }
28 38  
... ... @@ -30,7 +40,7 @@ public class EmqxServiceImpl implements EmqxService {
30 40 public void subscribe(MqttClient client) {
31 41 try {
32 42 // 订阅默认主题,可以根据需要修改
33   -// client.subscribe("$share/yudao/+/+/#", 1);
  43 + client.subscribe("/sys/+/+/#", 1);
34 44 log.info("订阅默认主题成功");
35 45 } catch (Exception e) {
36 46 log.error("订阅默认主题失败", e);
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.device;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceSaveReqVO;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceStatusUpdateReqVO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  8 +import jakarta.validation.Valid;
  9 +
  10 +/**
  11 + * IoT 设备数据 Service 接口
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +public interface IotDeviceDataService {
  16 +
  17 +
  18 + /**
  19 + * 保存设备数据
  20 + *
  21 + * @param productKey 产品 key
  22 + * @param deviceName 设备名称
  23 + * @param message 消息
  24 + */
  25 + void saveDeviceData(String productKey, String deviceName, String message);
  26 +}
0 27 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.device;
  2 +
  3 +import cn.hutool.json.JSONObject;
  4 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
  6 +import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService;
  7 +import jakarta.annotation.Resource;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.stereotype.Service;
  10 +
  11 +@Slf4j
  12 +@Service
  13 +public class IotDeviceDataServiceImpl implements IotDeviceDataService {
  14 +
  15 + @Resource
  16 + private IotDeviceService deviceService;
  17 + @Resource
  18 + private IotThingModelMessageService thingModelMessageService;
  19 +
  20 + @Override
  21 + public void saveDeviceData(String productKey, String deviceName, String message) {
  22 + // 1. 根据产品 key 和设备名称,获得设备信息
  23 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(productKey, deviceName);
  24 + // 2. 解析消息,保存数据
  25 + JSONObject jsonObject = new JSONObject(message);
  26 + log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", productKey, deviceName, jsonObject);
  27 + ThingModelMessage thingModelMessage = ThingModelMessage.builder()
  28 + .id(jsonObject.getStr("id"))
  29 + .sys(jsonObject.get("sys"))
  30 + .method(jsonObject.getStr("method"))
  31 + .params(jsonObject.get("params"))
  32 + .time(jsonObject.getLong("time") == null ? System.currentTimeMillis() : jsonObject.getLong("time"))
  33 + .productKey(productKey)
  34 + .deviceName(deviceName)
  35 + .deviceKey(device.getDeviceKey())
  36 + .build();
  37 + thingModelMessageService.saveThingModelMessage(thingModelMessage);
  38 + }
  39 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
... ... @@ -64,4 +64,13 @@ public interface IotDeviceService {
64 64 * @return 设备数量
65 65 */
66 66 Long getDeviceCountByProductId(Long productId);
  67 +
  68 + /**
  69 + * 根据产品 key 和设备名称,获得设备信息
  70 + *
  71 + * @param productKey 产品 key
  72 + * @param deviceName 设备名称
  73 + * @return 设备信息
  74 + */
  75 + IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName);
67 76 }
68 77 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
... ... @@ -4,6 +4,7 @@ import cn.hutool.core.util.IdUtil;
4 4 import cn.hutool.core.util.StrUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
7 8 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO;
8 9 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceSaveReqVO;
9 10 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceStatusUpdateReqVO;
... ... @@ -32,7 +33,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
32 33 @Service
33 34 @Validated
34 35 @Slf4j
35   -public class DeviceServiceImpl implements IotDeviceService {
  36 +public class IotDeviceServiceImpl implements IotDeviceService {
36 37  
37 38 @Resource
38 39 private IotDeviceMapper deviceMapper;
... ... @@ -224,4 +225,10 @@ public class DeviceServiceImpl implements IotDeviceService {
224 225 return deviceMapper.selectCountByProductId(productId);
225 226 }
226 227  
  228 + @Override
  229 + @TenantIgnore
  230 + public IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName) {
  231 + return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
  232 + }
  233 +
227 234 }
228 235 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java
... ... @@ -25,7 +25,7 @@ import java.util.stream.Collectors;
25 25 public class IotDbStructureDataServiceImpl implements IotDbStructureDataService {
26 26  
27 27 @Resource
28   - private TdEngineService tdEngineService;
  28 + private IotTdEngineService iotTdEngineService;
29 29  
30 30 @Resource
31 31 private TdRestApi tdRestApi;
... ... @@ -87,7 +87,7 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
87 87  
88 88 // 5. 创建超级表
89 89 String dataBaseName = url.substring(url.lastIndexOf("/") + 1);
90   - tdEngineService.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName);
  90 + iotTdEngineService.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName);
91 91 }
92 92  
93 93 @Override
... ... @@ -107,7 +107,7 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
107 107 private List<TdFieldDO> getTableFields(String tableName) {
108 108 List<TdFieldDO> fields = new ArrayList<>();
109 109 // 获取超级表的描述信息
110   - List<Map<String, Object>> maps = tdEngineService.describeSuperTable(url.substring(url.lastIndexOf("/") + 1), tableName);
  110 + List<Map<String, Object>> maps = iotTdEngineService.describeSuperTable(url.substring(url.lastIndexOf("/") + 1), tableName);
111 111 if (maps != null) {
112 112 // 过滤掉 note 字段为 TAG 的记录
113 113 maps = maps.stream().filter(map -> !"TAG".equals(map.get("note"))).toList();
... ... @@ -133,16 +133,16 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
133 133 String dataBaseName = url.substring(url.lastIndexOf("/") + 1);
134 134 // 添加新增字段
135 135 if (CollUtil.isNotEmpty(addFields)) {
136   - tdEngineService.addColumnForSuperTable(dataBaseName,tableName, addFields);
  136 + iotTdEngineService.addColumnForSuperTable(dataBaseName, tableName, addFields);
137 137 }
138 138 // 删除旧字段
139 139 if (CollUtil.isNotEmpty(dropFields)) {
140   - tdEngineService.dropColumnForSuperTable(dataBaseName,tableName, dropFields);
  140 + iotTdEngineService.dropColumnForSuperTable(dataBaseName, tableName, dropFields);
141 141 }
142 142 // 修改字段(先删除再添加)
143 143 if (CollUtil.isNotEmpty(modifyFields)) {
144   - tdEngineService.dropColumnForSuperTable(dataBaseName,tableName, modifyFields);
145   - tdEngineService.addColumnForSuperTable(dataBaseName,tableName, modifyFields);
  144 + iotTdEngineService.dropColumnForSuperTable(dataBaseName, tableName, modifyFields);
  145 + iotTdEngineService.addColumnForSuperTable(dataBaseName, tableName, modifyFields);
146 146 }
147 147 }
148 148  
... ... @@ -181,7 +181,7 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
181 181  
182 182 String superTableName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey());
183 183 String dataBaseName = url.substring(url.lastIndexOf("/") + 1);
184   - Integer tableExists = tdEngineService.checkSuperTableExists(dataBaseName, superTableName);
  184 + Integer tableExists = iotTdEngineService.checkSuperTableExists(dataBaseName, superTableName);
185 185  
186 186 if (tableExists != null && tableExists > 0) {
187 187 updateSuperTable(thingModel, product.getDeviceType());
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineService.java
... ... @@ -12,7 +12,7 @@ import java.util.Map;
12 12 /**
13 13 * TdEngineService
14 14 */
15   -public interface TdEngineService {
  15 +public interface IotTdEngineService {
16 16  
17 17 /**
18 18 * 创建数据库
... ... @@ -45,20 +45,20 @@ public interface TdEngineService {
45 45 /**
46 46 * 为超级表添加列
47 47 *
48   - * @param dataBaseName 数据库名称
  48 + * @param dataBaseName 数据库名称
49 49 * @param superTableName 超级表名称
50 50 * @param fieldsVo 字段信息
51 51 */
52   - void addColumnForSuperTable(String dataBaseName,String superTableName, List<TdFieldDO> fieldsVo);
  52 + void addColumnForSuperTable(String dataBaseName, String superTableName, List<TdFieldDO> fieldsVo);
53 53  
54 54 /**
55 55 * 为超级表删除列
56 56 *
57   - * @param dataBaseName 数据库名称
  57 + * @param dataBaseName 数据库名称
58 58 * @param superTableName 超级表名称
59 59 * @param fieldsVo 字段信息
60 60 */
61   - void dropColumnForSuperTable(String dataBaseName,String superTableName, List<TdFieldDO> fieldsVo);
  61 + void dropColumnForSuperTable(String dataBaseName, String superTableName, List<TdFieldDO> fieldsVo);
62 62  
63 63 /**
64 64 * 为超级表添加tag
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineServiceImpl.java
... ... @@ -15,7 +15,7 @@ import java.util.Map;
15 15  
16 16 @Service
17 17 @Slf4j
18   -public class TdEngineServiceImpl implements TdEngineService {
  18 +public class IotTdEngineServiceImpl implements IotTdEngineService {
19 19  
20 20 @Resource
21 21 private TdEngineMapper tdEngineMapper;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.tdengine;
  2 +
  3 +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
  4 +
  5 +/**
  6 + * 物模型消息 Service
  7 + */
  8 +public interface IotThingModelMessageService {
  9 +
  10 + /**
  11 + * 保存物模型消息
  12 + *
  13 + * @param thingModelMessage 物模型消息
  14 + */
  15 + void saveThingModelMessage(ThingModelMessage thingModelMessage);
  16 +}
0 17 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.tdengine;
  2 +
  3 +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
  4 +import org.springframework.stereotype.Service;
  5 +
  6 +@Service
  7 +public class IotThingModelMessageServiceImpl implements IotThingModelMessageService {
  8 +
  9 + @Override
  10 + public void saveThingModelMessage(ThingModelMessage thingModelMessage) {
  11 + // TODO 芋艿,后续实现
  12 + }
  13 +}
0 14 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml
... ... @@ -2,9 +2,10 @@
2 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 3  
4 4 <mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper">
  5 + <!-- TODO 对 $ 符号有安全要求的话,后期改为接口方式 -->
5 6  
6 7 <update id="createDatabase" parameterType="String">
7   - create database if not exists #{dataBaseName}
  8 + CREATE DATABASE IF NOT EXISTS ${dataBaseName}
8 9 </update>
9 10  
10 11 <update id="createSuperTable">
... ... @@ -271,7 +272,8 @@
271 272 <select id="getCountByTimestamp" parameterType="cn.iocoder.yudao.module.iot.domain.SelectDto"
272 273 resultType="java.util.Map">
273 274 SELECT count(0) AS count
274   - FROM #{dataBaseName}.#{tableName} WHERE ${fieldName} BETWEEN #{startTime} AND #{endTime}
  275 + FROM #{dataBaseName}.#{tableName}
  276 + WHERE ${fieldName} BETWEEN #{startTime} AND #{endTime}
275 277 </select>
276 278  
277 279 <select id="showSuperTables" resultType="java.util.Map">
... ... @@ -287,26 +289,30 @@
287 289 <select id="getLastDataByTags" parameterType="cn.iocoder.yudao.module.iot.domain.TagsSelectDao"
288 290 resultType="Map">
289 291 select last(*)
290   - from #{dataBaseName}.#{stableName} group by ${tagsName}
  292 + from #{dataBaseName}.#{stableName}
  293 + group by ${tagsName}
291 294 </select>
292 295  
293 296 <select id="getHistoryData" resultType="java.util.Map"
294 297 parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
295 298 SELECT #{fieldName}, ts
296   - FROM #{dataBaseName}.#{tableName} WHERE ts BETWEEN #{startTime} AND #{endTime}
  299 + FROM #{dataBaseName}.#{tableName}
  300 + WHERE ts BETWEEN #{startTime} AND #{endTime}
297 301 LIMIT #{num}
298 302 </select>
299 303  
300 304 <select id="getRealtimeData" resultType="java.util.Map"
301 305 parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
302 306 SELECT #{fieldName}, ts
303   - FROM #{dataBaseName}.#{tableName} LIMIT #{num}
  307 + FROM #{dataBaseName}.#{tableName}
  308 + LIMIT #{num}
304 309 </select>
305 310  
306 311 <select id="getAggregateData" resultType="java.util.Map"
307 312 parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
308 313 SELECT #{aggregate}(${fieldName})
309   - FROM #{dataBaseName}.#{tableName} WHERE ts BETWEEN #{startTime} AND #{endTime} interval (${interval})
  314 + FROM #{dataBaseName}.#{tableName}
  315 + WHERE ts BETWEEN #{startTime} AND #{endTime} interval (${interval})
310 316 LIMIT #{num}
311 317 </select>
312 318  
... ...