【功能优化】Bpm:完善设备属性的历史值

This commit is contained in:
YunaiV 2025-01-28 10:23:47 +08:00
parent 8c90448670
commit dfa03d24fd
10 changed files with 52 additions and 196 deletions

View File

@ -1,13 +1,13 @@
package cn.iocoder.yudao.module.iot.controller.admin.device; package cn.iocoder.yudao.module.iot.controller.admin.device;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
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.data.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceDataRespVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotTimeDataRespVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
@ -42,9 +42,10 @@ public class IotDevicePropertyController {
@Resource @Resource
private IotDeviceService deviceService; private IotDeviceService deviceService;
// TODO @芋艿权限
@GetMapping("/latest") @GetMapping("/latest")
@Operation(summary = "获取设备属性最新属性") @Operation(summary = "获取设备属性最新属性")
public CommonResult<List<IotDeviceDataRespVO>> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO pageReqVO) { public CommonResult<List<IotDevicePropertyRespVO>> getLatestDeviceProperties(@Valid IotDevicePropertyPageReqVO pageReqVO) {
Map<String, IotDevicePropertyDO> properties = devicePropertyService.getLatestDeviceProperties(pageReqVO); Map<String, IotDevicePropertyDO> properties = devicePropertyService.getLatestDeviceProperties(pageReqVO);
// 拼接数据 // 拼接数据
@ -58,18 +59,20 @@ public class IotDevicePropertyController {
return null; return null;
} }
IotDevicePropertyDO property = entry.getValue(); IotDevicePropertyDO property = entry.getValue();
return BeanUtils.toBean(thingModel, IotDeviceDataRespVO.class) return BeanUtils.toBean(thingModel, IotDevicePropertyRespVO.class)
.setDataType(thingModel.getProperty().getDataType()) .setDataType(thingModel.getProperty().getDataType())
.setValue(property.getValue()).setUpdateTime(property.getUpdateTime()); .setValue(property.getValue())
.setUpdateTime(LocalDateTimeUtil.toEpochMilli(property.getUpdateTime()));
})); }));
} }
// TODO @浩浩这里的 /history-page 包括方法名 // TODO @芋艿权限
@GetMapping("/history") @GetMapping("/history-page")
@Operation(summary = "获取设备属性历史数据") @Operation(summary = "获取设备属性历史数据")
public CommonResult<PageResult<IotTimeDataRespVO>> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO pageReqVO) { public CommonResult<PageResult<IotDevicePropertyRespVO>> getHistoryDevicePropertyPage(
PageResult<Map<String, Object>> list = devicePropertyService.getHistoryDeviceProperties(pageReqVO); @Valid IotDevicePropertyPageReqVO pageReqVO) {
return success(BeanUtils.toBean(list, IotTimeDataRespVO.class)); Assert.notEmpty(pageReqVO.getIdentifier(), "标识符不能为空");
return success(devicePropertyService.getHistoryDevicePropertyPage(pageReqVO));
} }
} }

View File

@ -5,14 +5,17 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
@Schema(description = "管理后台 - IoT 设备数据 Request VO") @Schema(description = "管理后台 - IoT 设备属性 Request VO")
@Data @Data
public class IotDeviceDataPageReqVO extends PageParam { public class IotDevicePropertyPageReqVO extends PageParam {
@Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
@NotNull(message = "设备编号不能为空") @NotNull(message = "设备编号不能为空")
private Long deviceId; private Long deviceId;
@Schema(description = "设备 Key", hidden = true)
private String deviceKey; // 非前端传递后端自己查询设置
@Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED)
private String identifier; private String identifier;

View File

@ -3,11 +3,9 @@ package cn.iocoder.yudao.module.iot.controller.admin.device.vo.data;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IoT 设备属性 Response VO") @Schema(description = "管理后台 - IoT 设备属性 Response VO")
@Data @Data
public class IotDeviceDataRespVO { public class IotDevicePropertyRespVO {
@Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED)
private String identifier; private String identifier;
@ -16,13 +14,10 @@ public class IotDeviceDataRespVO {
private Object value; private Object value;
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime updateTime; private Long updateTime;
// ========== 基于 ThingModel 查询 ========== // ========== 基于 ThingModel 查询 ==========
// @Schema(description = "物模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816")
// private Long thingModelId;
@Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED)
private String name; private String name;

View File

@ -1,23 +0,0 @@
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.data;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class IotTimeDataRespVO {
/**
* 时间
*/
private long time;
/**
* 数据值
*/
private Object data;
}

View File

@ -1,71 +0,0 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
import lombok.Data;
import java.util.Map;
// TODO @haohao类似 SelectDO 的想法只是它是返回ps貌似可以在 tdengine 里面创建一个 query 放这种比较特殊的查询和结果对象dataobject 更多还是实际存储的结构化的 do
@Data
@Deprecated
public class SelectVisualDO {
/**
* 数据库名称
*/
private String dataBaseName;
/**
* 表名
*/
private String tableName;
private String deviceKey;
/**
* 属性
*/
private String fieldName;
/**
* 查询类型0历史数据1实时数据2聚合数据
*/
private int type;
/**
* 查询的数据量
*/
private int num;
/**
* 聚合函数
*/
private String aggregate;
/**
* 统计间隔数字+s/m/h/d
* 比如1s,1m,1h,1d代表1秒1分钟1小时1天
*/
private String interval;
/**
* 开始时间
*/
private Long startTime;
/**
* 结束时间
*/
private Long endTime;
/**
* 请求参数
*/
private Map<String, Object> params;
private String sql;
private String deviceId;
private Long lastTime;
}

View File

@ -44,14 +44,6 @@ public interface IotDeviceLogMapper {
* @return 设备日志列表 * @return 设备日志列表
*/ */
IPage<IotDeviceLogDO> selectPage(IPage<IotDeviceLogDO> page, IPage<IotDeviceLogDO> selectPage(IPage<IotDeviceLogDO> page,
@Param("reqVO") IotDeviceLogPageReqVO reqVO); @Param("reqVO") IotDeviceLogPageReqVO reqVO);
/**
* 获得设备日志总数
*
* @param reqVO 查询条件
* @return 日志总数
*/
Long selectCount(@Param("reqVO") IotDeviceLogPageReqVO reqVO);
} }

View File

@ -3,12 +3,13 @@ package cn.iocoder.yudao.module.iot.dal.tdengine;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO;
import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField;
import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -83,23 +84,7 @@ public interface IotDevicePropertyMapper {
@Param("properties") Map<String, Object> properties, @Param("properties") Map<String, Object> properties,
@Param("reportTime") Long reportTime); @Param("reportTime") Long reportTime);
// TODO @芋艿待实现 IPage<IotDevicePropertyRespVO> selectPageByHistory(IPage<?> page,
/** @Param("reqVO") IotDevicePropertyPageReqVO reqVO);
* 获取历史数据列表
*
* @param selectVisualDO 查询条件
* @return 历史数据列表
*/
@TenantIgnore
List<Map<String, Object>> selectHistoryDataList(SelectVisualDO selectVisualDO);
/**
* 获取历史数据条数
*
* @param selectVisualDO 查询条件
* @return 数据条数
*/
@TenantIgnore
Long selectHistoryCount(SelectVisualDO selectVisualDO);
} }

View File

@ -1,7 +1,8 @@
package cn.iocoder.yudao.module.iot.service.device.data; package cn.iocoder.yudao.module.iot.service.device.data;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
import jakarta.validation.Valid; import jakarta.validation.Valid;
@ -35,14 +36,14 @@ public interface IotDevicePropertyService {
* @param deviceId 设备编号 * @param deviceId 设备编号
* @return 设备属性最新数据 * @return 设备属性最新数据
*/ */
Map<String, IotDevicePropertyDO> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceId); Map<String, IotDevicePropertyDO> getLatestDeviceProperties(@Valid IotDevicePropertyPageReqVO deviceId);
/** /**
* 获得设备属性历史数据 * 获得设备属性历史数据
* *
* @param deviceDataReqVO 设备属性历史数据 Request VO * @param pageReqVO 分页请求
* @return 设备属性历史数据 * @return 设备属性历史数据
*/ */
PageResult<Map<String, Object>> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO); PageResult<IotDevicePropertyRespVO> getHistoryDevicePropertyPage(@Valid IotDevicePropertyPageReqVO pageReqVO);
} }

View File

@ -6,7 +6,8 @@ import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
@ -21,6 +22,8 @@ import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.product.IotProductService;
import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -150,8 +153,9 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
entry -> IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build())); entry -> IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build()));
} }
// TODO @芋艿需要在优化下根据 name 之类的过滤
@Override @Override
public Map<String, IotDevicePropertyDO> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO pageReqVO) { public Map<String, IotDevicePropertyDO> getLatestDeviceProperties(@Valid IotDevicePropertyPageReqVO pageReqVO) {
// 获取设备信息 // 获取设备信息
IotDeviceDO device = deviceService.validateDeviceExists(pageReqVO.getDeviceId()); IotDeviceDO device = deviceService.validateDeviceExists(pageReqVO.getDeviceId());
@ -160,34 +164,15 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
} }
@Override @Override
public PageResult<Map<String, Object>> getHistoryDeviceProperties(IotDeviceDataPageReqVO deviceDataReqVO) { public PageResult<IotDevicePropertyRespVO> getHistoryDevicePropertyPage(IotDevicePropertyPageReqVO pageReqVO) {
// PageResult<Map<String, Object>> pageResult = new PageResult<>(); // 获取设备信息
// // 1. 获取设备信息 IotDeviceDO device = deviceService.validateDeviceExists(pageReqVO.getDeviceId());
// IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); pageReqVO.setDeviceKey(device.getDeviceKey());
// // 2. 获取设备属性历史数据
// SelectVisualDO selectVisualDO = new SelectVisualDO(); // TODO @芋艿增加一个表不存在的 try catch
// selectVisualDO.setDataBaseName(getDatabaseName()); IPage<IotDevicePropertyRespVO> page = devicePropertyMapper.selectPageByHistory(
// selectVisualDO.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())); new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO);
// selectVisualDO.setDeviceKey(device.getDeviceKey()); return new PageResult<>(page.getRecords(), page.getTotal());
// selectVisualDO.setFieldName(deviceDataReqVO.getIdentifier());
// selectVisualDO.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
// selectVisualDO.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
// Map<String, Object> params = new HashMap<>();
// params.put("rows", deviceDataReqVO.getPageSize());
// params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize());
// selectVisualDO.setParams(params);
// pageResult.setList(devicePropertyDataMapper.selectHistoryDataList(selectVisualDO));
// pageResult.setTotal(devicePropertyDataMapper.selectHistoryCount(selectVisualDO));
// return pageResult;
return null; // TODO 芋艿晚点实现
} }
// private String getDatabaseName() {
// return StrUtil.subAfter(url, "/", true);
// }
//
// private static String getDeviceTableName(String productKey, String deviceName) {
// return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey, deviceName);
// }
} }

View File

@ -45,7 +45,6 @@
DROP COLUMN ${field.field} DROP COLUMN ${field.field}
</update> </update>
<!-- TODO 芋艿report_time 需要增加下 -->
<insert id="insert"> <insert id="insert">
INSERT INTO device_property_${device.deviceKey} INSERT INTO device_property_${device.deviceKey}
USING product_property_${device.productKey} USING product_property_${device.productKey}
@ -63,29 +62,16 @@
) )
</insert> </insert>
<!-- 描述超级表结构 -->
<select id="describeSuperTable" resultType="java.util.Map"> <select id="describeSuperTable" resultType="java.util.Map">
DESCRIBE product_property_${productKey} DESCRIBE product_property_${productKey}
</select> </select>
<!-- 获取历史数据 --> <!-- TODO 芋艿:缺少时间范围 AND ts BETWEEN #{reqVO.startTime} AND #{reqVO.endTime} -->
<select id="selectHistoryDataList" resultType="java.util.Map" <select id="selectPageByHistory" resultType="cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO">
parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO"> SELECT ${reqVO.identifier} AS `value`, report_time AS update_time
SELECT ${fieldName} AS data, ts AS time FROM device_property_${reqVO.deviceKey}
FROM device_property_${deviceKey} WHERE ${reqVO.identifier} IS NOT NULL
WHERE ts BETWEEN #{startTime} AND #{endTime}
AND ${fieldName} IS NOT NULL
ORDER BY ts DESC ORDER BY ts DESC
LIMIT #{params.rows} OFFSET #{params.page}
</select>
<!-- 统计历史数据总数 -->
<select id="selectHistoryCount" resultType="java.lang.Long"
parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO">
SELECT COUNT(*)
FROM device_property_${deviceKey}
WHERE ts BETWEEN #{startTime} AND #{endTime}
AND ${fieldName} IS NOT NULL
</select> </select>
</mapper> </mapper>