feat(设备指标): 支持批量获取设备属性指标数据

This commit is contained in:
zhouhao 2026-03-05 16:32:10 +08:00
parent 858dab5529
commit df8b6874ee
2 changed files with 128 additions and 15 deletions

View File

@ -15,6 +15,8 @@
*/
package org.jetlinks.community.things.impl.metric;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
import org.hswebframework.web.crud.events.EntityCreatedEvent;
@ -33,13 +35,16 @@ import org.jetlinks.community.gateway.annotation.Subscribe;
import org.jetlinks.community.things.impl.entity.PropertyMetricEntity;
import org.jetlinks.community.things.metric.AbstractPropertyMetricManager;
import org.springframework.context.event.EventListener;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class DefaultPropertyMetricManager extends AbstractPropertyMetricManager {
@ -79,22 +84,105 @@ public class DefaultPropertyMetricManager extends AbstractPropertyMetricManager
.map(PropertyMetadataConstants.Metrics::getMetrics)
.orElse(Collections.emptyList()))
.collectMap(PropertyMetric::getId, Function.identity(), LinkedHashMap::new),
(exists, inMetadata) -> {
for (Map.Entry<String, PropertyMetric> entry : exists.entrySet()) {
String metric = entry.getKey();
PropertyMetric independent = entry.getValue();
PropertyMetric fromMetadata = inMetadata.get(metric);
if (fromMetadata == null) {
inMetadata.put(metric, independent);
continue;
}
fromMetadata.setValue(independent.getValue());
}
return Flux.fromIterable(inMetadata.values());
})
this::merge)
.flatMapMany(Function.identity());
}
/**
* 批量获取指标
*
* @param thingType 物类型
* @param thingId 物ID
* @param properties 属性列表
* @return 指标信息
*/
public Flux<DevicePropertyMetricInfo> getPropertyMetrics(String thingType,
String thingId,
List<String> properties) {
if (CollectionUtils.isEmpty(properties)) {
return Flux.empty();
}
//数据库中记录的
Mono<Map<String, Map<String, PropertyMetric>>> existsByProperty = repository
.createQuery()
.where(PropertyMetricEntity::getThingType, thingType)
.and(PropertyMetricEntity::getThingId, thingId)
.in(PropertyMetricEntity::getProperty, properties)
.fetch()
.groupBy(PropertyMetricEntity::getProperty)
.flatMap(group -> group
.map(PropertyMetricEntity::toMetric)
.collectMap(PropertyMetric::getId, Function.identity())
.map(map -> Map.entry(group.key(), map)))
.collectMap(Map.Entry::getKey, Map.Entry::getValue);
//物模型中配置的
Mono<Map<String, Map<String, PropertyMetric>>> metadataByProperty = registry
.getThing(thingType, thingId)
.flatMap(Thing::getTemplate)
.flatMap(ThingTemplate::getMetadata)
.flatMapMany(metadata -> Flux
.fromIterable(properties)
.map(prop -> Map.entry(
prop,
metadata
.getProperty(prop)
.map(PropertyMetadataConstants.Metrics::getMetrics)
.orElse(Collections.emptyList())
)))
.flatMap(entry -> Flux
.fromIterable(entry.getValue())
.collectMap(PropertyMetric::getId, Function.identity())
.map(map -> Map.entry(entry.getKey(), map)))
.collectMap(Map.Entry::getKey, Map.Entry::getValue, LinkedHashMap::new);
return Mono
.zip(existsByProperty, metadataByProperty)
.flatMapMany(tuple -> {
Map<String, Map<String, PropertyMetric>> exists = tuple.getT1();
Map<String, Map<String, PropertyMetric>> metadata = tuple.getT2();
return Flux
.fromIterable(properties)
.flatMap(prop -> {
Map<String, PropertyMetric> existsMetrics = exists.getOrDefault(prop, Collections.emptyMap());
Map<String, PropertyMetric> metadataMetrics = new LinkedHashMap<>(metadata.getOrDefault(prop, Collections.emptyMap()));
return merge(existsMetrics, metadataMetrics)
.collectList()
.map(metrics -> new DevicePropertyMetricInfo(prop, metrics));
});
});
}
@Getter
public static class DevicePropertyMetricInfo {
private final String property;
private final List<PropertyMetric> metrics;
public DevicePropertyMetricInfo(String property, List<PropertyMetric> metrics) {
this.property = property;
this.metrics = metrics;
}
}
private Flux<PropertyMetric> merge(Map<String, PropertyMetric> exists,
Map<String, PropertyMetric> inMetadata) {
for (Map.Entry<String, PropertyMetric> entry : exists.entrySet()) {
String metric = entry.getKey();
PropertyMetric independent = entry.getValue();
PropertyMetric fromMetadata = inMetadata.get(metric);
if (fromMetadata == null) {
inMetadata.put(metric, independent);
continue;
}
fromMetadata.setValue(independent.getValue());
}
return Flux.fromIterable(inMetadata.values());
}
@Transactional
public Mono<SaveResult> savePropertyMetrics(String thingType,
String thingId,
String property,
@ -112,7 +200,22 @@ public class DefaultPropertyMetricManager extends AbstractPropertyMetricManager
entity.genericId();
return entity;
})
.as(repository::save);
.collectList()
.flatMap(list -> {
List<String> ids = list.stream().map(PropertyMetricEntity::getId).collect(Collectors.toList());
return repository
.createDelete()
.where(PropertyMetricEntity::getThingType, thingType)
.and(PropertyMetricEntity::getThingId, thingId)
.and(PropertyMetricEntity::getProperty, property)
// 删除当前物模型中的其他指标
.when(CollectionUtils.isNotEmpty(ids),
delete -> delete.notIn(PropertyMetricEntity::getId, ids))
.execute()
.then(
repository.save(list)
);
});
}
@ -171,4 +274,4 @@ public class DefaultPropertyMetricManager extends AbstractPropertyMetricManager
.publish("/_sys/thing-property-metric/clear-cache", key)
.then();
}
}
}

View File

@ -1149,6 +1149,16 @@ public class DeviceInstanceController implements
.getPropertyMetrics(DeviceThingType.device.getId(), deviceId, property);
}
@PostMapping("/{deviceId}/metric/properties")
@Operation(summary = "批量获取设备属性指标数据")
@QueryAction
public Flux<DefaultPropertyMetricManager.DevicePropertyMetricInfo> getPropertyMetrics(@PathVariable String deviceId,
@RequestBody Mono<List<String>> payload) {
return payload
.flatMapMany(properties -> metricManager
.getPropertyMetrics(DeviceThingType.device.getId(), deviceId, properties));
}
//仅解析文件为属性物模型
@PostMapping(value = "/{productId}/property-metadata/file/analyze")
@SaveAction