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

# Conflicts:
#	yudao-server/src/main/resources/application-local.yaml
This commit is contained in:
YunaiV 2025-01-08 22:37:33 +08:00
commit 16120820a0
24 changed files with 617 additions and 102 deletions

View File

@ -3,19 +3,19 @@ package cn.iocoder.yudao.module.iot.controller.admin.device;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
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.device.IotDeviceSaveReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.*;
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.dal.dataobject.device.IotDeviceLogDO;
import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService;
import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@ -31,6 +31,12 @@ public class IotDeviceDataController {
@Resource
private IotDevicePropertyDataService deviceDataService;
@Resource
private IotDeviceLogDataService iotDeviceLogDataService;
@Resource
private IotDeviceLogDataService deviceLogDataService;
// TODO @浩浩这里的 /latest-list包括方法名
@GetMapping("/latest")
@Operation(summary = "获取设备属性最新数据")
@ -46,5 +52,20 @@ public class IotDeviceDataController {
PageResult<Map<String, Object>> list = deviceDataService.getHistoryDeviceProperties(deviceDataReqVO);
return success(BeanUtils.toBean(list, IotTimeDataRespVO.class));
}
// TODO:数据权限
@PostMapping("/simulator")
@Operation(summary = "模拟设备")
public CommonResult<Boolean> simulatorDevice(@Valid @RequestBody IotDeviceDataSimulatorSaveReqVO simulatorReqVO) {
//TODO:先生成一下设备日志 后续完善模拟设备代码逻辑
iotDeviceLogDataService.createDeviceLog(simulatorReqVO);
return success(true);
}
// TODO:数据权限
@GetMapping("/log/page")
@Operation(summary = "获得设备日志分页")
public CommonResult<PageResult<IotDeviceLogRespVO>> getDeviceLogPage(@Valid IotDeviceLogPageReqVO pageReqVO) {
PageResult<IotDeviceLogDO> pageResult = deviceLogDataService.getDeviceLogPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, IotDeviceLogRespVO.class));
}
}

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.util.Set;
@ -13,6 +14,7 @@ public class IotDeviceSaveReqVO {
private Long id;
@Schema(description = "设备编号", requiredMode = Schema.RequiredMode.AUTO, example = "177")
@Size(max = 50, message = "设备编号长度不能超过50个字符")
private String deviceKey;
@Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")

View File

@ -0,0 +1,42 @@
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 org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@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 Long reportTime;
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - IoT 设备日志分页查询 Request VO")
@Data
public class IotDeviceLogPageReqVO extends PageParam {
@Schema(description = "设备标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "device123")
@NotEmpty(message = "设备标识不能为空")
private String deviceKey;
@Schema(description = "消息类型", example = "property")
private String type;
@Schema(description = "标识符", example = "temperature")
private String subType;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IoT 设备日志 Response VO")
@Data
public class IotDeviceLogRespVO {
@Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String id;
@Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "product123")
private String productKey;
@Schema(description = "设备标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "device123")
private String deviceKey;
@Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "property")
private String type;
@Schema(description = "标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature")
private String subType;
@Schema(description = "日志内容", requiredMode = Schema.RequiredMode.REQUIRED)
private String content;
@Schema(description = "上报时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime reportTime;
@Schema(description = "记录时间戳", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime ts;
}

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelRespVO;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO;
@ -73,6 +74,14 @@ public class IotThingModelController {
return success(IotThingModelConvert.INSTANCE.convertList(list));
}
@GetMapping("/list")
@Operation(summary = "获得产品物模型列表")
@PreAuthorize("@ss.hasPermission('iot:thing-model:query')")
public CommonResult<List<IotThingModelRespVO>> getThingModelListByProductId(@Valid IotThingModelListReqVO reqVO) {
List<IotThingModelDO> list = thingModelService.getThingModelList(reqVO);
return success(IotThingModelConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@Operation(summary = "获得产品物模型分页")
@PreAuthorize("@ss.hasPermission('iot:thing-model:query')")

View File

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - IoT 产品物模型List Request VO")
@Data
public class IotThingModelListReqVO {
@Schema(description = "功能标识")
private String identifier;
@Schema(description = "功能名称", example = "张三")
private String name;
@Schema(description = "功能类型", example = "1")
@InEnum(IotThingModelTypeEnum.class)
private Integer type;
@Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "产品ID不能为空")
private Long productId;
}

View File

@ -0,0 +1,62 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.device;
import cn.hutool.core.date.DateTime;
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 Long reportTime;
/**
* 时序时间
*/
private Long ts;
}

View File

@ -6,6 +6,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
// TODO @芋艿纠结下字段
@Deprecated
/**
* TD 物模型消息日志的数据库
*/
@ -15,21 +16,7 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
public class ThingModelMessageDO {
/**
* 数据库名称
*/
private String dataBaseName;
// TODO @haohaosuperTableName tableName 是不是合并因为每个 mapper 操作的时候有且只会使用到其中一个
/**
* 超级表名称
*/
private String superTableName;
/**
* 表名称
*/
private String tableName;
/**
* 消息 ID
@ -37,9 +24,11 @@ public class ThingModelMessageDO {
private String id;
/**
* 扩展功能的参数
* 系统扩展参数
*
* 例如设备状态系统时间固件版本等系统级信息
*/
private Object sys;
private Object system;
/**
* 请求方法
@ -58,6 +47,12 @@ public class ThingModelMessageDO {
*/
private Long time;
/**
* 设备信息
*/
private String productKey;
/**
* 设备 key
*/

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.dal.mysql.thingmodel;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
import org.apache.ibatis.annotations.Mapper;
@ -28,6 +29,17 @@ public interface IotThingModelMapper extends BaseMapperX<IotThingModelDO> {
.orderByDesc(IotThingModelDO::getId));
}
default List<IotThingModelDO> selectList(IotThingModelListReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<IotThingModelDO>()
.eqIfPresent(IotThingModelDO::getIdentifier, reqVO.getIdentifier())
.likeIfPresent(IotThingModelDO::getName, reqVO.getName())
.eqIfPresent(IotThingModelDO::getType, reqVO.getType())
.eqIfPresent(IotThingModelDO::getProductId, reqVO.getProductId())
// TODO @芋艿看看要不要加枚举
.notIn(IotThingModelDO::getIdentifier, "get", "set", "post")
.orderByDesc(IotThingModelDO::getId));
}
default IotThingModelDO selectByProductIdAndIdentifier(Long productId, String identifier) {
return selectOne(IotThingModelDO::getProductId, productId,
IotThingModelDO::getIdentifier, identifier);

View File

@ -0,0 +1,61 @@
package cn.iocoder.yudao.module.iot.dal.tdengine;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* IOT 设备日志数据 Mapper 接口
*
* 基于 TDengine 实现设备日志的存储
*/
@Mapper
@TDengineDS
@InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析因为 JSqlParser TDengine SQL 解析会报错
public interface IotDeviceLogDataMapper {
/**
* 创建设备日志超级表
*
* 注意初始化时只需创建一次
*/
void createDeviceLogSTable();
/**
* 创建设备日志子表
*
* @param deviceKey 设备标识
*/
void createDeviceLogTable(@Param("deviceKey") String deviceKey);
/**
* 插入设备日志数据
*
* 如果子表不存在会自动创建子表
*
* @param log 设备日志数据
*/
void insert(@Param("log") IotDeviceLogDO log);
/**
* 获得设备日志分页
*
* @param reqVO 分页查询条件
* @return 设备日志列表
*/
List<IotDeviceLogDO> selectPage(@Param("reqVO") IotDeviceLogPageReqVO reqVO);
/**
* 获得设备日志总数
*
* @param reqVO 查询条件
* @return 日志总数
*/
Long selectCount(@Param("reqVO") IotDeviceLogPageReqVO reqVO);
}

View File

@ -1,29 +1,35 @@
package cn.iocoder.yudao.module.iot.dal.tdengine;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessageDO;
import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* 处理 TD 中物模型消息日志的操作
*/
@Mapper
@DS("tdengine")
@Deprecated
@TDengineDS
@InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析因为 JSqlParser TDengine SQL 解析会报错
public interface TdThingModelMessageMapper {
/**
* 创建物模型消息日志超级表超级表
*
*/
@TenantIgnore
void createSuperTable(ThingModelMessageDO superTable);
void createSuperTable(@Param("productKey") String productKey);
/**
* 创建子表
*
*/
@TenantIgnore
void createTableWithTag(ThingModelMessageDO table);
void createTableWithTag(@Param("productKey") String productKey,@Param("deviceKey") String deviceKey);
}

View File

@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.iot.framework.tdengine.config;
import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService;
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;
}
}
}

View File

@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.iot.service.device;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
/**
* IoT 设备日志数据 Service 接口
*
* @author alwayssuper
*/
public interface IotDeviceLogDataService {
/**
* 初始化 TDengine 超级表
*
*系统启动时会自动初始化一次
*/
void initTDengineSTable();
/**
* 插入设备日志
*
* 当该设备第一次插入日志时自动创建该设备的设备日志子表
*
* @param simulatorReqVO 设备日志模拟数据
*/
void createDeviceLog(IotDeviceDataSimulatorSaveReqVO simulatorReqVO);
/**
* 获得设备日志分页
*
* @param pageReqVO 分页查询
* @return 设备日志分页
*/
PageResult<IotDeviceLogDO> getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO);
}

View File

@ -0,0 +1,68 @@
package cn.iocoder.yudao.module.iot.service.device;
import cn.hutool.core.date.DateTime;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
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.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO;
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;
import java.time.ZoneId;
import java.util.List;
/**
* IoT 设备日志数据 Service 实现了
*
* @author alwayssuper
*/
@Service
@Slf4j
@Validated
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) {
//TODO:讨论一下iotkit这块TS和上报时间都是外部传入的 但是看TDengine文档 他是建议对TS在SQL中直接NOW 咱们的TS数据获取是走哪一种
// 1. 转换请求对象为 DO
IotDeviceLogDO iotDeviceLogDO = BeanUtils.toBean(simulatorReqVO, IotDeviceLogDO.class);
// 2. 处理时间字段
long currentTime = System.currentTimeMillis();
// 2.1 设置时序时间为当前时间
iotDeviceLogDO.setTs(currentTime);
// 3. 插入数据
iotDeviceLogDataMapper.insert(iotDeviceLogDO);
}
@Override
public PageResult<IotDeviceLogDO> getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) {
// 查询数据
List<IotDeviceLogDO> list = iotDeviceLogDataMapper.selectPage(pageReqVO);
Long total = iotDeviceLogDataMapper.selectCount(pageReqVO);
// 构造分页结果
return new PageResult<>(list, total);
}
}

View File

@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdThingModelMessageMapper;
import cn.iocoder.yudao.module.iot.enums.IotConstants;
import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum;
import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
@ -85,6 +86,7 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe
@Resource
private IotDevicePropertyDataMapper devicePropertyDataMapper;
@Override
public void defineDevicePropertyData(Long productId) {
// 1.1 查询产品和物模型
@ -109,6 +111,7 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe
return;
}
newFields.add(0, new TDengineTableField(TDengineTableField.FIELD_TS, TDengineTableField.TYPE_TIMESTAMP));
// 2.1.1 创建产品超级表
devicePropertyDataMapper.createProductPropertySTable(product.getProductKey(), newFields);
return;
}

View File

@ -34,9 +34,6 @@ public class IotProductServiceImpl implements IotProductService {
@Resource
private IotProductMapper productMapper;
@Resource
@Lazy // 延迟加载解决循环依赖
private IotThingModelMessageService thingModelMessageService;
@Resource
@Lazy // 延迟加载解决循环依赖
private IotDevicePropertyDataService devicePropertyDataService;
@ -125,8 +122,7 @@ public class IotProductServiceImpl implements IotProductService {
if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) {
// 3.1 创建产品超级表数据模型
devicePropertyDataService.defineDevicePropertyData(id);
// 3.2 创建物模型日志超级表数据模型 TODO 待定message 要不要分
thingModelMessageService.createSuperTable(id);
}
productMapper.updateById(updateObj);
}

View File

@ -16,11 +16,6 @@ public interface IotThingModelMessageService {
*/
void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage);
/**
* 创建物模型消息日志超级表
*
* @param productId 产品编号
*/
void createSuperTable(Long productId);
}

View File

@ -7,15 +7,13 @@ import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdThingModelMessageMapper;
import cn.iocoder.yudao.module.iot.enums.IotConstants;
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
@ -63,6 +61,9 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
@Resource
private TdEngineDMLMapper tdEngineDMLMapper;
@Resource
private TdThingModelMessageMapper tdThingModelMessageMapper;
@Resource
private DeviceDataRedisDAO deviceDataRedisDAO;
@ -77,8 +78,6 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey());
iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO()
.setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus()));
// 1.2 创建物模型日志设备表
createThingModelMessageDeviceTable(device.getProductKey(), device.getDeviceName(), device.getDeviceKey());
}
// 2. 获取设备属性并进行物模型校验过滤非物模型属性
@ -107,32 +106,34 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType()));
}
@Override
@TenantIgnore
public void createSuperTable(Long productId) {
// 1. 查询产品
IotProductDO product = productService.getProduct(productId);
// 2. 获取超级表的名称和数据库名称
// TODO @alwayssuper最好 databaseNamesuperTableName 的处理放到 tdThinkModelMessageMapper 可以考虑弄个 default 方法
String databaseName = IotTdDatabaseUtils.getDatabaseName(url);
String superTableName = IotTdDatabaseUtils.getThingModelMessageSuperTableName(product.getProductKey());
// 解析物模型获取字段列表
List<TdFieldDO> schemaFields = List.of(
TdFieldDO.builder().fieldName("time").dataType("TIMESTAMP").build(),
TdFieldDO.builder().fieldName("id").dataType("NCHAR").dataLength(64).build(),
TdFieldDO.builder().fieldName("sys").dataType("NCHAR").dataLength(2048).build(),
TdFieldDO.builder().fieldName("method").dataType("NCHAR").dataLength(256).build(),
TdFieldDO.builder().fieldName("params").dataType("NCHAR").dataLength(2048).build()
);
// 设置超级表的标签
List<TdFieldDO> tagsFields = List.of(
TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build()
);
// 3. 创建超级表
tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields));
}
// @Override
// @TenantIgnore
// public void createSuperTable(Long productId) {
// // 1. 查询产品
// IotProductDO product = productService.getProduct(productId);
// // 2. 创建日志超级表
// tdThingModelMessageMapper.createSuperTable(product.getProductKey());
//
// // 2. 获取超级表的名称和数据库名称
// // TODO @alwayssuper最好 databaseNamesuperTableName 的处理放到 tdThinkModelMessageMapper 可以考虑弄个 default 方法
//// String databaseName = IotTdDatabaseUtils.getDatabaseName(url);
//// String superTableName = IotTdDatabaseUtils.getThingModelMessageSuperTableName(product.getProductKey());
////
//// // 解析物模型获取字段列表
//// List<TdFieldDO> schemaFields = List.of(
//// TdFieldDO.builder().fieldName("time").dataType("TIMESTAMP").build(),
//// TdFieldDO.builder().fieldName("id").dataType("NCHAR").dataLength(64).build(),
//// TdFieldDO.builder().fieldName("sys").dataType("NCHAR").dataLength(2048).build(),
//// TdFieldDO.builder().fieldName("method").dataType("NCHAR").dataLength(256).build(),
//// TdFieldDO.builder().fieldName("params").dataType("NCHAR").dataLength(2048).build()
//// );
//// // 设置超级表的标签
//// List<TdFieldDO> tagsFields = List.of(
//// TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build()
//// );
//// // 3. 创建超级表
//// tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields));
// }
private List<IotThingModelDO> getValidFunctionList(String productKey) {
return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey),
@ -228,29 +229,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
.setTags(tagsFieldValues));
}
/**
* 创建物模型日志设备数据表
*
* @param productKey 产品 Key
* @param deviceName 设备名称
* @param deviceKey 设备 Key
*
*/
private void createThingModelMessageDeviceTable(String productKey, String deviceName, String deviceKey){
// 1. 获取超级表的名称数据库名称设备日志表名称
String databaseName = IotTdDatabaseUtils.getDatabaseName(url);
String superTableName = IotTdDatabaseUtils.getThingModelMessageSuperTableName(productKey);
// TODO @alwayssuper最好 databaseNamesuperTableNamethinkModelMessageDeviceTableName 的处理放到 tdThinkModelMessageMapper 可以考虑弄个 default 方法
String thinkModelMessageDeviceTableName = IotTdDatabaseUtils.getThingModelMessageDeviceTableName(productKey, deviceName);
// 2. 创建物模型日志设备数据表
// tdThingModelMessageMapper.createTableWithTag(ThingModelMessageDO.builder().build()
// .setDataBaseName(databaseName)
// .setSuperTableName(superTableName)
// .setTableName(thinkModelMessageDeviceTableName)
// .setDeviceKey(deviceKey));
}
/**
* 获取数据库名称

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.iot.service.thingmodel;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
@ -69,4 +70,11 @@ public interface IotThingModelService {
*/
List<IotThingModelDO> getProductThingModelListByProductKey(String productKey);
/**
* 获得产品物模型列表
*
* @param reqVO 列表查询
* @return 产品物模型列表
*/
List<IotThingModelDO> getThingModelList(IotThingModelListReqVO reqVO);
}

View File

@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelParam;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO;
import cn.iocoder.yudao.module.iot.convert.thingmodel.IotThingModelConvert;
@ -134,6 +135,11 @@ public class IotThingModelServiceImpl implements IotThingModelService {
return thingModelMapper.selectListByProductKey(productKey);
}
@Override
public List<IotThingModelDO> getThingModelList(IotThingModelListReqVO reqVO) {
return thingModelMapper.selectList(reqVO);
}
/**
* 校验功能是否存在
*

View File

@ -0,0 +1,78 @@
<?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>
<select id="selectPage" resultType="cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO">
SELECT ts, id, device_key, product_key, type, subType, content, report_time
FROM device_log_${reqVO.deviceKey}
<where>
<if test="reqVO.type != null and reqVO.type != ''">
AND type = #{reqVO.type}
</if>
<if test="reqVO.subType != null and reqVO.subType != ''">
AND subType = #{reqVO.subType}
</if>
<if test="reqVO.createTime != null">
AND ts BETWEEN #{reqVO.createTime[0]} AND #{reqVO.createTime[1]}
</if>
</where>
ORDER BY ts DESC
LIMIT #{reqVO.pageSize} OFFSET #{reqVO.pageNo}
</select>
<select id="selectCount" resultType="Long">
SELECT COUNT(*)
FROM device_log_${reqVO.deviceKey}
<where>
<if test="reqVO.type != null and reqVO.type != ''">
AND type = #{reqVO.type}
</if>
<if test="reqVO.subType != null and reqVO.subType != ''">
AND subType = #{reqVO.subType}
</if>
<if test="reqVO.createTime != null">
AND ts BETWEEN #{reqVO.createTime[0]} AND #{reqVO.createTime[1]}
</if>
</where>
</select>
</mapper>

View File

@ -42,7 +42,7 @@
<!-- 根据标签获取最新数据 -->
<select id="selectLastDataListByTags" parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TagsSelectDO"
resultType="Map">
resultType="java.util.Map">
SELECT LAST(*)
FROM ${dataBaseName}.${stableName}
GROUP BY ${tagsName}

View File

@ -6,26 +6,28 @@
<!-- 创建物模型消息日志超级表 -->
<update id="createSuperTable">
CREATE STABLE ${dataBaseName}.${superTableName}(
CREATE STABLE thing_model_message_${productKey}(
ts TIMESTAMP,
id VARCHAR(255),
sys VARCHAR(2048),
method VARCHAR(255),
params VARCHAR(2048)
id NCHAR(64),
sys NCHAR(2048),
method NCHAR(255),
params NCHAR(2048),
device_name NCHAR(64)
)TAGS (
device_key VARCHAR(255)
device_key NCHAR(50)
)
</update>
<!-- 创建物模型消息日志子表带有deviceKey的TAG -->
<update id="createTableWithTag">
CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName}
USING ${dataBaseName}.${superTableName}(
CREATE STABLE ${deviceKey}
USING thing_model_message_${productKey}(
ts,
id ,
sys ,
method ,
params
params ,
device_name
)TAGS(
#{device_key}
)