mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-10-31 02:28:03 +08:00 
			
		
		
		
	feat:simulator
This commit is contained in:
		| @ -3,19 +3,21 @@ package cn.iocoder.yudao.module.iot.controller.admin.device; | |||||||
| import cn.iocoder.yudao.framework.common.pojo.CommonResult; | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||||
| import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||||||
|  | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO; | ||||||
| import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; | ||||||
| import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; | ||||||
|  | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; | ||||||
| import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; | ||||||
| import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO; | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO; | ||||||
|  | import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService; | ||||||
| import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; | import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; | ||||||
| import io.swagger.v3.oas.annotations.Operation; | import io.swagger.v3.oas.annotations.Operation; | ||||||
| import io.swagger.v3.oas.annotations.tags.Tag; | import io.swagger.v3.oas.annotations.tags.Tag; | ||||||
| import jakarta.annotation.Resource; | import jakarta.annotation.Resource; | ||||||
| import jakarta.validation.Valid; | import jakarta.validation.Valid; | ||||||
|  | import org.springframework.security.access.prepost.PreAuthorize; | ||||||
| import org.springframework.validation.annotation.Validated; | import org.springframework.validation.annotation.Validated; | ||||||
| import org.springframework.web.bind.annotation.GetMapping; | import org.springframework.web.bind.annotation.*; | ||||||
| import org.springframework.web.bind.annotation.RequestMapping; |  | ||||||
| import org.springframework.web.bind.annotation.RestController; |  | ||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| @ -31,6 +33,9 @@ public class IotDeviceDataController { | |||||||
|     @Resource |     @Resource | ||||||
|     private IotDevicePropertyDataService deviceDataService; |     private IotDevicePropertyDataService deviceDataService; | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private IotDeviceLogDataService iotDeviceLogDataService; | ||||||
|  |  | ||||||
|     // TODO @浩浩:这里的 /latest-list,包括方法名。 |     // TODO @浩浩:这里的 /latest-list,包括方法名。 | ||||||
|     @GetMapping("/latest") |     @GetMapping("/latest") | ||||||
|     @Operation(summary = "获取设备属性最新数据") |     @Operation(summary = "获取设备属性最新数据") | ||||||
| @ -47,4 +52,12 @@ public class IotDeviceDataController { | |||||||
|         return success(BeanUtils.toBean(list, IotTimeDataRespVO.class)); |         return success(BeanUtils.toBean(list, IotTimeDataRespVO.class)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @PostMapping("/simulator") | ||||||
|  |     @Operation(summary = "模拟设备") | ||||||
|  |     public CommonResult<Boolean> simulatorDevice(@Valid @RequestBody IotDeviceDataSimulatorSaveReqVO simulatorReqVO) { | ||||||
|  |         //TODO:先生成一下日志  后续完善模拟设备代码逻辑 | ||||||
|  |         iotDeviceLogDataService.createDeviceLog(simulatorReqVO); | ||||||
|  |         return success(true); | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
| @ -0,0 +1,40 @@ | |||||||
|  | package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData; | ||||||
|  |  | ||||||
|  | import io.swagger.v3.oas.annotations.media.Schema; | ||||||
|  | import jakarta.validation.constraints.NotEmpty; | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import java.time.LocalDateTime; | ||||||
|  |  | ||||||
|  | @Schema(description = "管理后台 - IoT 模拟设备数据 Request VO") | ||||||
|  | @Data | ||||||
|  | public class IotDeviceDataSimulatorSaveReqVO { | ||||||
|  |  | ||||||
|  |     @Schema(description = "消息ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "msg123") | ||||||
|  |     private String id; | ||||||
|  |  | ||||||
|  |     @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "product123") | ||||||
|  |     @NotEmpty(message = "产品ID不能为空") | ||||||
|  |     private String productKey; | ||||||
|  |  | ||||||
|  |     @Schema(description = "设备ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "device123") | ||||||
|  |     @NotEmpty(message = "设备ID不能为空") | ||||||
|  |     private String deviceKey; | ||||||
|  |  | ||||||
|  |     @Schema(description = "消息/日志类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "property") | ||||||
|  |     @NotEmpty(message = "消息类型不能为空") | ||||||
|  |     private String type; | ||||||
|  |  | ||||||
|  |     @Schema(description = "标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature") | ||||||
|  |     @NotEmpty(message = "标识符不能为空") | ||||||
|  |     private String subType; | ||||||
|  |  | ||||||
|  |     @Schema(description = "数据内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "{\"value\": 25.6}") | ||||||
|  |     @NotEmpty(message = "数据内容不能为空") | ||||||
|  |     private String content; | ||||||
|  |  | ||||||
|  |     @Schema(description = "上报时间", requiredMode = Schema.RequiredMode.REQUIRED) | ||||||
|  |     private LocalDateTime reportTime; | ||||||
|  |  | ||||||
|  | } | ||||||
| @ -1,5 +1,62 @@ | |||||||
| package cn.iocoder.yudao.module.iot.dal.dataobject.device;/** | package cn.iocoder.yudao.module.iot.dal.dataobject.device; | ||||||
|  * @Author alwayssuper |  | ||||||
|  * @Date 2025/1/5 12:32 | import cn.hutool.core.date.DateTime; | ||||||
| */public class IotDeviceLogDO { | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Builder; | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  |  | ||||||
|  | import java.time.LocalDateTime; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * IoT 设备日志数据 DO | ||||||
|  |  * | ||||||
|  |  * @author alwayssuper | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | @Builder | ||||||
|  | @NoArgsConstructor | ||||||
|  | @AllArgsConstructor | ||||||
|  | public class IotDeviceLogDO { | ||||||
|  |     /** | ||||||
|  |      * 消息ID | ||||||
|  |      */ | ||||||
|  |     private String id; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 产品ID | ||||||
|  |      */ | ||||||
|  |     private String productKey; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 设备ID | ||||||
|  |      */ | ||||||
|  |     private String deviceKey; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 消息/日志类型 | ||||||
|  |      */ | ||||||
|  |     private String type; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 标识符:用于标识具体的属性、事件或服务 | ||||||
|  |      */ | ||||||
|  |     private String subType; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 数据内容:存储具体的消息数据内容,通常是JSON格式 | ||||||
|  |      */ | ||||||
|  |     private String content; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 上报时间戳 | ||||||
|  |      */ | ||||||
|  |     private DateTime reportTime; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 时序时间 | ||||||
|  |      */ | ||||||
|  |     private DateTime ts; | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ import org.apache.ibatis.annotations.Param; | |||||||
|  * 处理 TD 中物模型消息日志的操作 |  * 处理 TD 中物模型消息日志的操作 | ||||||
|  */ |  */ | ||||||
| @Mapper | @Mapper | ||||||
|  | @Deprecated | ||||||
| @TDengineDS | @TDengineDS | ||||||
| @InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 | @InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 | ||||||
| public interface TdThingModelMessageMapper { | public interface TdThingModelMessageMapper { | ||||||
|  | |||||||
| @ -1,5 +1,40 @@ | |||||||
| package cn.iocoder.yudao.module.iot.framework.tdengine.config;/** | package cn.iocoder.yudao.module.iot.framework.tdengine.config; | ||||||
|  * @Author alwayssuper |  | ||||||
|  * @Date 2025/1/5 17:37 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService; | ||||||
| */public class TDengineTableInitConfiguration { | import lombok.RequiredArgsConstructor; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.springframework.boot.ApplicationArguments; | ||||||
|  | import org.springframework.boot.ApplicationRunner; | ||||||
|  | import org.springframework.context.annotation.Configuration; | ||||||
|  | import org.springframework.core.annotation.Order; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * TDengine 表初始化的 Configuration | ||||||
|  |  * | ||||||
|  |  * @author alwayssuper | ||||||
|  |  */ | ||||||
|  | @Slf4j | ||||||
|  | @RequiredArgsConstructor | ||||||
|  | @Configuration | ||||||
|  | @Order(Integer.MAX_VALUE) // 保证在最后执行 | ||||||
|  | public class TDengineTableInitConfiguration implements ApplicationRunner { | ||||||
|  |  | ||||||
|  |     private final IotDeviceLogDataService deviceLogService; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void run(ApplicationArguments args) { | ||||||
|  |         try { | ||||||
|  |             // 初始化设备日志表 | ||||||
|  |             deviceLogService.initTDengineSTable(); | ||||||
|  |             log.info("初始化 设备日志表 TDengine 表结构成功"); | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             if (ex.getMessage().contains("Table already exists")) { | ||||||
|  |                 log.info("TDengine 设备日志超级表已存在,跳过创建"); | ||||||
|  |                 return; | ||||||
|  |             }else{ | ||||||
|  |                 log.error("初始化 设备日志表 TDengine  表结构失败", ex); | ||||||
|  |             } | ||||||
|  |             throw ex; | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,5 +1,45 @@ | |||||||
| package cn.iocoder.yudao.module.iot.service.device;/** | package cn.iocoder.yudao.module.iot.service.device; | ||||||
|  * @Author alwayssuper |  | ||||||
|  * @Date 2025/1/5 11:50 | import cn.hutool.core.date.DateTime; | ||||||
| */public class IotDeviceLogDataServiceImpl { | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||||||
|  | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; | ||||||
|  | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; | ||||||
|  | import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogDataMapper; | ||||||
|  | import jakarta.annotation.Resource; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.springframework.stereotype.Service; | ||||||
|  | import org.springframework.validation.annotation.Validated; | ||||||
|  |  | ||||||
|  | import java.time.LocalDateTime; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * IoT 设备日志数据 Service 实现了 | ||||||
|  |  * | ||||||
|  |  * @author alwayssuper | ||||||
|  |  */ | ||||||
|  | @Service | ||||||
|  | @Slf4j | ||||||
|  | public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{ | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private IotDeviceLogDataMapper iotDeviceLogDataMapper; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void initTDengineSTable() { | ||||||
|  |         try { | ||||||
|  |             // 创建设备日志超级表 | ||||||
|  |             iotDeviceLogDataMapper.createDeviceLogSTable(); | ||||||
|  |             log.info("创建设备日志超级表成功"); | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             throw ex; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void createDeviceLog(IotDeviceDataSimulatorSaveReqVO simulatorReqVO) { | ||||||
|  |         IotDeviceLogDO iotDeviceLogDO = BeanUtils.toBean(simulatorReqVO, IotDeviceLogDO.class); | ||||||
|  |         iotDeviceLogDO.setTs(DateTime.now()); | ||||||
|  |         iotDeviceLogDataMapper.insert(iotDeviceLogDO); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -85,8 +85,6 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe | |||||||
|     @Resource |     @Resource | ||||||
|     private IotDevicePropertyDataMapper devicePropertyDataMapper; |     private IotDevicePropertyDataMapper devicePropertyDataMapper; | ||||||
|  |  | ||||||
|     @Resource |  | ||||||
|     private TdThingModelMessageMapper tdThingModelMessageMapper; |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void defineDevicePropertyData(Long productId) { |     public void defineDevicePropertyData(Long productId) { | ||||||
| @ -114,8 +112,6 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe | |||||||
|             newFields.add(0, new TDengineTableField(TDengineTableField.FIELD_TS, TDengineTableField.TYPE_TIMESTAMP)); |             newFields.add(0, new TDengineTableField(TDengineTableField.FIELD_TS, TDengineTableField.TYPE_TIMESTAMP)); | ||||||
|             // 2.1.1 创建产品超级表 |             // 2.1.1 创建产品超级表 | ||||||
|             devicePropertyDataMapper.createProductPropertySTable(product.getProductKey(), newFields); |             devicePropertyDataMapper.createProductPropertySTable(product.getProductKey(), newFields); | ||||||
|             // 2.1.2 创建物模型日志超级表 |  | ||||||
|             tdThingModelMessageMapper.createSuperTable(product.getProductKey()); |  | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         // 2.2 情况二:如果是修改的时候,需要更新表 |         // 2.2 情况二:如果是修改的时候,需要更新表 | ||||||
|  | |||||||
| @ -78,8 +78,6 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ | |||||||
|             createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); |             createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); | ||||||
|             iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO() |             iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO() | ||||||
|                     .setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus())); |                     .setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus())); | ||||||
|             // 1.2 创建物模型日志设备表 |  | ||||||
|             tdThingModelMessageMapper.createTableWithTag(device.getProductKey(), device.getDeviceKey()); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 |         // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 | ||||||
| @ -231,16 +229,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ | |||||||
|                 .setTags(tagsFieldValues)); |                 .setTags(tagsFieldValues)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 创建物模型日志设备数据表 |  | ||||||
|      * |  | ||||||
|      * @param productKey 产品 Key |  | ||||||
|      * @param deviceKey  设备 Key |  | ||||||
|      * |  | ||||||
|      */ |  | ||||||
|     private void createThingModelMessageDeviceTable(String productKey,  String deviceKey){ |  | ||||||
|         tdThingModelMessageMapper.createTableWithTag(productKey, deviceKey); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取数据库名称 |      * 获取数据库名称 | ||||||
|  | |||||||
| @ -0,0 +1,44 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!DOCTYPE mapper | ||||||
|  |         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | ||||||
|  |         "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | ||||||
|  | <mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogDataMapper"> | ||||||
|  |  | ||||||
|  |     <!-- 创建设备日志超级表   初始化只创建一次--> | ||||||
|  |     <update id="createDeviceLogSTable"> | ||||||
|  |         CREATE STABLE device_log( | ||||||
|  |         ts TIMESTAMP, | ||||||
|  |         id NCHAR(50), | ||||||
|  |         product_key NCHAR(50), | ||||||
|  |         type NCHAR(50), | ||||||
|  |         subType NCHAR(50), | ||||||
|  |         content NCHAR(1024), | ||||||
|  |         report_time TIMESTAMP | ||||||
|  |         )TAGS ( | ||||||
|  |         device_key NCHAR(50) | ||||||
|  |         ) | ||||||
|  |     </update> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     <!-- 创建设备日志子表  讨论:TDengine 在子表不存在的情况下 可在数据插入时 自动建表  要不要去掉创建子表的逻辑  由第一次插入数据时自动创建--> | ||||||
|  |     <update id="createDeviceLogTable"> | ||||||
|  |         CREATE TABLE device_log_${deviceKey} USING device_log TAGS('${deviceKey}') | ||||||
|  |     </update> | ||||||
|  |  | ||||||
|  |     <!-- 插入设备日志数据 在子表不存在的情况下 可在数据插入时 自动建表 --> | ||||||
|  |     <insert id="insert"> | ||||||
|  |         INSERT INTO device_log_${log.deviceKey} (ts, id, product_key, type, subType, content, report_time) | ||||||
|  |         USING device_log | ||||||
|  |         TAGS ('${log.deviceKey}') | ||||||
|  |         VALUES ( | ||||||
|  |             #{log.ts}, | ||||||
|  |             #{log.id}, | ||||||
|  |             #{log.productKey}, | ||||||
|  |             #{log.type}, | ||||||
|  |             #{log.subType}, | ||||||
|  |             #{log.content}, | ||||||
|  |             #{log.reportTime} | ||||||
|  |         ) | ||||||
|  |     </insert> | ||||||
|  |  | ||||||
|  | </mapper> | ||||||
		Reference in New Issue
	
	Block a user
	 alwayssuper
					alwayssuper